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

hlpfile.c
Go to the documentation of this file.
00001 /*
00002  * Help Viewer
00003  *
00004  * Copyright    1996 Ulrich Schmid
00005  *              2002, 2008 Eric Pouech
00006  *              2007 Kirill K. Smirnov
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00021  */
00022 
00023 #include <stdarg.h>
00024 #include <stdio.h>
00025 #include <string.h>
00026 
00027 #include "windef.h"
00028 #include "winbase.h"
00029 #include "wingdi.h"
00030 #include "winuser.h"
00031 #include "winhelp.h"
00032 
00033 #include "wine/debug.h"
00034 
00035 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
00036 
00037 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i)
00038 {
00039     return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1];
00040 }
00041 
00042 static inline short GET_SHORT(const BYTE* buffer, unsigned i)
00043 {
00044     return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1];
00045 }
00046 
00047 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i)
00048 {
00049     return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2);
00050 }
00051 
00052 static HLPFILE *first_hlpfile = 0;
00053 
00054 
00055 /**************************************************************************
00056  * HLPFILE_BPTreeSearch
00057  *
00058  * Searches for an element in B+ tree
00059  *
00060  * PARAMS
00061  *     buf        [I] pointer to the embedded file structured as a B+ tree
00062  *     key        [I] pointer to data to find
00063  *     comp       [I] compare function
00064  *
00065  * RETURNS
00066  *     Pointer to block identified by key, or NULL if failure.
00067  *
00068  */
00069 static void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
00070                            HLPFILE_BPTreeCompare comp)
00071 {
00072     unsigned magic;
00073     unsigned page_size;
00074     unsigned cur_page;
00075     unsigned level;
00076     BYTE *pages, *ptr, *newptr;
00077     int i, entries;
00078     int ret;
00079 
00080     magic = GET_USHORT(buf, 9);
00081     if (magic != 0x293B)
00082     {
00083         WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
00084         return NULL;
00085     }
00086     page_size = GET_USHORT(buf, 9+4);
00087     cur_page  = GET_USHORT(buf, 9+26);
00088     level     = GET_USHORT(buf, 9+32);
00089     pages     = buf + 9 + 38;
00090     while (--level > 0)
00091     {
00092         ptr = pages + cur_page*page_size;
00093         entries = GET_SHORT(ptr, 2);
00094         ptr += 6;
00095         for (i = 0; i < entries; i++)
00096         {
00097             if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
00098             ptr = newptr;
00099         }
00100         cur_page = GET_USHORT(ptr-2, 0);
00101     }
00102     ptr = pages + cur_page*page_size;
00103     entries = GET_SHORT(ptr, 2);
00104     ptr += 8;
00105     for (i = 0; i < entries; i++)
00106     {
00107         ret = comp(ptr, key, 1, (void **)&newptr);
00108         if (ret == 0) return ptr;
00109         if (ret > 0) return NULL;
00110         ptr = newptr;
00111     }
00112     return NULL;
00113 }
00114 
00115 /**************************************************************************
00116  * HLPFILE_BPTreeEnum
00117  *
00118  * Enumerates elements in B+ tree.
00119  *
00120  * PARAMS
00121  *     buf        [I]  pointer to the embedded file structured as a B+ tree
00122  *     cb         [I]  compare function
00123  *     cookie     [IO] cookie for cb function
00124  */
00125 void HLPFILE_BPTreeEnum(BYTE* buf, HLPFILE_BPTreeCallback cb, void* cookie)
00126 {
00127     unsigned magic;
00128     unsigned page_size;
00129     unsigned cur_page;
00130     unsigned level;
00131     BYTE *pages, *ptr, *newptr;
00132     int i, entries;
00133 
00134     magic = GET_USHORT(buf, 9);
00135     if (magic != 0x293B)
00136     {
00137         WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
00138         return;
00139     }
00140     page_size = GET_USHORT(buf, 9+4);
00141     cur_page  = GET_USHORT(buf, 9+26);
00142     level     = GET_USHORT(buf, 9+32);
00143     pages     = buf + 9 + 38;
00144     while (--level > 0)
00145     {
00146         ptr = pages + cur_page*page_size;
00147         cur_page = GET_USHORT(ptr, 4);
00148     }
00149     while (cur_page != 0xFFFF)
00150     {
00151         ptr = pages + cur_page*page_size;
00152         entries = GET_SHORT(ptr, 2);
00153         ptr += 8;
00154         for (i = 0; i < entries; i++)
00155         {
00156             cb(ptr, (void **)&newptr, cookie);
00157             ptr = newptr;
00158         }
00159         cur_page = GET_USHORT(pages+cur_page*page_size, 6);
00160     }
00161 }
00162 
00163 
00164 /***********************************************************************
00165  *
00166  *           HLPFILE_UncompressedLZ77_Size
00167  */
00168 static INT HLPFILE_UncompressedLZ77_Size(const BYTE *ptr, const BYTE *end)
00169 {
00170     int  i, newsize = 0;
00171 
00172     while (ptr < end)
00173     {
00174         int mask = *ptr++;
00175         for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
00176     {
00177             if (mask & 1)
00178         {
00179                 int code = GET_USHORT(ptr, 0);
00180                 int len  = 3 + (code >> 12);
00181                 newsize += len;
00182                 ptr     += 2;
00183         }
00184             else newsize++, ptr++;
00185     }
00186     }
00187 
00188     return newsize;
00189 }
00190 
00191 /***********************************************************************
00192  *
00193  *           HLPFILE_UncompressLZ77
00194  */
00195 static BYTE *HLPFILE_UncompressLZ77(const BYTE *ptr, const BYTE *end, BYTE *newptr)
00196 {
00197     int i;
00198 
00199     while (ptr < end)
00200     {
00201         int mask = *ptr++;
00202         for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
00203     {
00204             if (mask & 1)
00205         {
00206                 int code   = GET_USHORT(ptr, 0);
00207                 int len    = 3 + (code >> 12);
00208                 int offset = code & 0xfff;
00209                 /*
00210                  * We must copy byte-by-byte here. We cannot use memcpy nor
00211                  * memmove here. Just example:
00212                  * a[]={1,2,3,4,5,6,7,8,9,10}
00213                  * newptr=a+2;
00214                  * offset=1;
00215                  * We expect:
00216                  * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
00217                  */
00218                 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
00219                 ptr    += 2;
00220         }
00221             else *newptr++ = *ptr++;
00222     }
00223     }
00224 
00225     return newptr;
00226 }
00227 
00228 /***********************************************************************
00229  *
00230  *           HLPFILE_Uncompress2
00231  */
00232 
00233 static void HLPFILE_Uncompress2(HLPFILE* hlpfile, const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
00234 {
00235     BYTE *phptr, *phend;
00236     UINT code;
00237     UINT index;
00238 
00239     while (ptr < end && newptr < newend)
00240     {
00241         if (!*ptr || *ptr >= 0x10)
00242             *newptr++ = *ptr++;
00243         else
00244     {
00245             code  = 0x100 * ptr[0] + ptr[1];
00246             index = (code - 0x100) / 2;
00247 
00248             phptr = (BYTE*)hlpfile->phrases_buffer + hlpfile->phrases_offsets[index];
00249             phend = (BYTE*)hlpfile->phrases_buffer + hlpfile->phrases_offsets[index + 1];
00250 
00251             if (newptr + (phend - phptr) > newend)
00252             {
00253                 WINE_FIXME("buffer overflow %p > %p for %lu bytes\n",
00254                            newptr, newend, (SIZE_T)(phend - phptr));
00255                 return;
00256             }
00257             memcpy(newptr, phptr, phend - phptr);
00258             newptr += phend - phptr;
00259             if (code & 1) *newptr++ = ' ';
00260 
00261             ptr += 2;
00262     }
00263     }
00264     if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
00265 }
00266 
00267 /******************************************************************
00268  *      HLPFILE_Uncompress3
00269  *
00270  *
00271  */
00272 static BOOL HLPFILE_Uncompress3(HLPFILE* hlpfile, char* dst, const char* dst_end,
00273                                 const BYTE* src, const BYTE* src_end)
00274 {
00275     unsigned int idx, len;
00276 
00277     for (; src < src_end; src++)
00278     {
00279         if ((*src & 1) == 0)
00280         {
00281             idx = *src / 2;
00282             if (idx > hlpfile->num_phrases)
00283             {
00284                 WINE_ERR("index in phrases %d/%d\n", idx, hlpfile->num_phrases);
00285                 len = 0;
00286             }
00287             else
00288             {
00289                 len = hlpfile->phrases_offsets[idx + 1] - hlpfile->phrases_offsets[idx];
00290                 if (dst + len <= dst_end)
00291                     memcpy(dst, &hlpfile->phrases_buffer[hlpfile->phrases_offsets[idx]], len);
00292             }
00293         }
00294         else if ((*src & 0x03) == 0x01)
00295         {
00296             idx = (*src + 1) * 64;
00297             idx += *++src;
00298             if (idx > hlpfile->num_phrases)
00299             {
00300                 WINE_ERR("index in phrases %d/%d\n", idx, hlpfile->num_phrases);
00301                 len = 0;
00302             }
00303             else
00304             {
00305                 len = hlpfile->phrases_offsets[idx + 1] - hlpfile->phrases_offsets[idx];
00306                 if (dst + len <= dst_end)
00307                     memcpy(dst, &hlpfile->phrases_buffer[hlpfile->phrases_offsets[idx]], len);
00308             }
00309         }
00310         else if ((*src & 0x07) == 0x03)
00311         {
00312             len = (*src / 8) + 1;
00313             if (dst + len <= dst_end)
00314                 memcpy(dst, src + 1, len);
00315             src += len;
00316         }
00317         else
00318         {
00319             len = (*src / 16) + 1;
00320             if (dst + len <= dst_end)
00321                 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
00322         }
00323         dst += len;
00324     }
00325 
00326     if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
00327     return TRUE;
00328 }
00329 
00330 /******************************************************************
00331  *      HLPFILE_UncompressRLE
00332  *
00333  *
00334  */
00335 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE* dst, unsigned dstsz)
00336 {
00337     BYTE        ch;
00338     BYTE*       sdst = dst + dstsz;
00339 
00340     while (src < end)
00341     {
00342         ch = *src++;
00343         if (ch & 0x80)
00344         {
00345             ch &= 0x7F;
00346             if (dst + ch <= sdst)
00347                 memcpy(dst, src, ch);
00348             src += ch;
00349         }
00350         else
00351         {
00352             if (dst + ch <= sdst)
00353                 memset(dst, (char)*src, ch);
00354             src++;
00355         }
00356         dst += ch;
00357     }
00358     if (dst != sdst)
00359         WINE_WARN("Buffer X-flow: d(%lu) instead of d(%u)\n",
00360                   (SIZE_T)(dst - (sdst - dstsz)), dstsz);
00361 }
00362 
00363 
00364 /******************************************************************
00365  *      HLPFILE_PageByOffset
00366  *
00367  *
00368  */
00369 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset, ULONG* relative)
00370 {
00371     HLPFILE_PAGE*       page;
00372     HLPFILE_PAGE*       found;
00373 
00374     if (!hlpfile) return 0;
00375 
00376     WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
00377 
00378     if (offset == 0xFFFFFFFF) return NULL;
00379     page = NULL;
00380 
00381     for (found = NULL, page = hlpfile->first_page; page; page = page->next)
00382     {
00383         if (page->offset <= offset && (!found || found->offset < page->offset))
00384         {
00385             *relative = offset - page->offset;
00386             found = page;
00387         }
00388     }
00389     if (!found)
00390         WINE_ERR("Page of offset %u not found in file %s\n",
00391                  offset, hlpfile->lpszPath);
00392     return found;
00393 }
00394 
00395 /***********************************************************************
00396  *
00397  *           HLPFILE_Contents
00398  */
00399 static HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile, ULONG* relative)
00400 {
00401     HLPFILE_PAGE*       page = NULL;
00402 
00403     if (!hlpfile) return NULL;
00404 
00405     page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start, relative);
00406     if (!page)
00407     {
00408         page = hlpfile->first_page;
00409         *relative = 0;
00410     }
00411     return page;
00412 }
00413 
00414 /**************************************************************************
00415  * comp_PageByHash
00416  *
00417  * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
00418  *
00419  */
00420 static int comp_PageByHash(void *p, const void *key,
00421                            int leaf, void** next)
00422 {
00423     LONG lKey = (LONG_PTR)key;
00424     LONG lTest = (INT)GET_UINT(p, 0);
00425 
00426     *next = (char *)p+(leaf?8:6);
00427     WINE_TRACE("Comparing '%d' with '%d'\n", lKey, lTest);
00428     if (lTest < lKey) return -1;
00429     if (lTest > lKey) return 1;
00430     return 0;
00431 }
00432 
00433 /***********************************************************************
00434  *
00435  *           HLPFILE_PageByHash
00436  */
00437 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash, ULONG* relative)
00438 {
00439     BYTE *ptr;
00440 
00441     if (!hlpfile) return NULL;
00442     if (!lHash) return HLPFILE_Contents(hlpfile, relative);
00443 
00444     WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
00445 
00446     /* For win 3.0 files hash values are really page numbers */
00447     if (hlpfile->version <= 16)
00448     {
00449         if (lHash >= hlpfile->wTOMapLen) return NULL;
00450         return HLPFILE_PageByOffset(hlpfile, hlpfile->TOMap[lHash], relative);
00451     }
00452 
00453     ptr = HLPFILE_BPTreeSearch(hlpfile->Context, LongToPtr(lHash), comp_PageByHash);
00454     if (!ptr)
00455     {
00456         WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
00457         return NULL;
00458     }
00459 
00460     return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4), relative);
00461 }
00462 
00463 /***********************************************************************
00464  *
00465  *           HLPFILE_PageByMap
00466  */
00467 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap, ULONG* relative)
00468 {
00469     unsigned int i;
00470 
00471     if (!hlpfile) return 0;
00472 
00473     WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
00474 
00475     for (i = 0; i < hlpfile->wMapLen; i++)
00476     {
00477         if (hlpfile->Map[i].lMap == lMap)
00478             return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset, relative);
00479     }
00480 
00481     WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
00482     return NULL;
00483 }
00484 
00485 /**************************************************************************
00486  * comp_FindSubFile
00487  *
00488  * HLPFILE_BPTreeCompare function for HLPFILE directory.
00489  *
00490  */
00491 static int comp_FindSubFile(void *p, const void *key,
00492                             int leaf, void** next)
00493 {
00494     *next = (char *)p+strlen(p)+(leaf?5:3);
00495     WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p, (const char *)key);
00496     return strcmp(p, key);
00497 }
00498 
00499 /***********************************************************************
00500  *
00501  *           HLPFILE_FindSubFile
00502  */
00503 static BOOL HLPFILE_FindSubFile(HLPFILE* hlpfile, LPCSTR name, BYTE **subbuf, BYTE **subend)
00504 {
00505     BYTE *ptr;
00506 
00507     WINE_TRACE("looking for file '%s'\n", name);
00508     ptr = HLPFILE_BPTreeSearch(hlpfile->file_buffer + GET_UINT(hlpfile->file_buffer, 4),
00509                                name, comp_FindSubFile);
00510     if (!ptr) return FALSE;
00511     *subbuf = hlpfile->file_buffer + GET_UINT(ptr, strlen(name)+1);
00512     if (*subbuf >= hlpfile->file_buffer + hlpfile->file_buffer_size)
00513     {
00514         WINE_ERR("internal file %s does not fit\n", name);
00515         return FALSE;
00516     }
00517     *subend = *subbuf + GET_UINT(*subbuf, 0);
00518     if (*subend > hlpfile->file_buffer + hlpfile->file_buffer_size)
00519     {
00520         WINE_ERR("internal file %s does not fit\n", name);
00521         return FALSE;
00522     }
00523     if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9)
00524     {
00525         WINE_ERR("invalid size provided for internal file %s\n", name);
00526         return FALSE;
00527     }
00528     return TRUE;
00529 }
00530 
00531 /***********************************************************************
00532  *
00533  *           HLPFILE_Hash
00534  */
00535 LONG HLPFILE_Hash(LPCSTR lpszContext)
00536 {
00537     LONG lHash = 0;
00538     CHAR c;
00539 
00540     while ((c = *lpszContext++))
00541     {
00542         CHAR x = 0;
00543         if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
00544         if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
00545         if (c >= '1' && c <= '9') x = c - '0';
00546         if (c == '0') x = 10;
00547         if (c == '.') x = 12;
00548         if (c == '_') x = 13;
00549         if (x) lHash = lHash * 43 + x;
00550     }
00551     return lHash;
00552 }
00553 
00554 static LONG fetch_long(const BYTE** ptr)
00555 {
00556     LONG        ret;
00557 
00558     if (*(*ptr) & 1)
00559     {
00560         ret = (*(const ULONG*)(*ptr) - 0x80000000) / 2;
00561         (*ptr) += 4;
00562     }
00563     else
00564     {
00565         ret = (*(const USHORT*)(*ptr) - 0x8000) / 2;
00566         (*ptr) += 2;
00567     }
00568 
00569     return ret;
00570 }
00571 
00572 static ULONG fetch_ulong(const BYTE** ptr)
00573 {
00574     ULONG        ret;
00575 
00576     if (*(*ptr) & 1)
00577     {
00578         ret = *(const ULONG*)(*ptr) / 2;
00579         (*ptr) += 4;
00580     }
00581     else
00582     {
00583         ret = *(const USHORT*)(*ptr) / 2;
00584         (*ptr) += 2;
00585     }
00586     return ret;
00587 }    
00588 
00589 static short fetch_short(const BYTE** ptr)
00590 {
00591     short       ret;
00592 
00593     if (*(*ptr) & 1)
00594     {
00595         ret = (*(const unsigned short*)(*ptr) - 0x8000) / 2;
00596         (*ptr) += 2;
00597     }
00598     else
00599     {
00600         ret = (*(const unsigned char*)(*ptr) - 0x80) / 2;
00601         (*ptr)++;
00602     }
00603     return ret;
00604 }
00605 
00606 static unsigned short fetch_ushort(const BYTE** ptr)
00607 {
00608     unsigned short ret;
00609 
00610     if (*(*ptr) & 1)
00611     {
00612         ret = *(const unsigned short*)(*ptr) / 2;
00613         (*ptr) += 2;
00614     }
00615     else
00616     {
00617         ret = *(const unsigned char*)(*ptr) / 2;
00618         (*ptr)++;
00619     }
00620     return ret;
00621 }
00622 
00623 /******************************************************************
00624  *      HLPFILE_DecompressGfx
00625  *
00626  * Decompress the data part of a bitmap or a metafile
00627  */
00628 static const BYTE*      HLPFILE_DecompressGfx(const BYTE* src, unsigned csz, unsigned sz, BYTE packing,
00629                                               BYTE** alloc)
00630 {
00631     const BYTE* dst;
00632     BYTE*       tmp;
00633     unsigned    sz77;
00634 
00635     WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
00636 
00637     switch (packing)
00638     {
00639     case 0: /* uncompressed */
00640         if (sz != csz)
00641             WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
00642         dst = src;
00643         *alloc = NULL;
00644         break;
00645     case 1: /* RunLen */
00646         dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz);
00647         if (!dst) return NULL;
00648         HLPFILE_UncompressRLE(src, src + csz, *alloc, sz);
00649         break;
00650     case 2: /* LZ77 */
00651         sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
00652         dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz77);
00653         if (!dst) return NULL;
00654         HLPFILE_UncompressLZ77(src, src + csz, *alloc);
00655         if (sz77 != sz)
00656             WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
00657         break;
00658     case 3: /* LZ77 then RLE */
00659         sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
00660         tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
00661         if (!tmp) return FALSE;
00662         HLPFILE_UncompressLZ77(src, src + csz, tmp);
00663         dst = *alloc = HeapAlloc(GetProcessHeap(), 0, sz);
00664         if (!dst)
00665         {
00666             HeapFree(GetProcessHeap(), 0, tmp);
00667             return FALSE;
00668         }
00669         HLPFILE_UncompressRLE(tmp, tmp + sz77, *alloc, sz);
00670         HeapFree(GetProcessHeap(), 0, tmp);
00671         break;
00672     default:
00673         WINE_FIXME("Unsupported packing %u\n", packing);
00674         return NULL;
00675     }
00676     return dst;
00677 }
00678 
00679 static BOOL HLPFILE_RtfAddRawString(struct RtfData* rd, const char* str, size_t sz)
00680 {
00681     if (rd->ptr + sz >= rd->data + rd->allocated)
00682     {
00683         char*   new = HeapReAlloc(GetProcessHeap(), 0, rd->data, rd->allocated *= 2);
00684         if (!new) return FALSE;
00685         rd->ptr = new + (rd->ptr - rd->data);
00686         rd->data = new;
00687     }
00688     memcpy(rd->ptr, str, sz);
00689     rd->ptr += sz;
00690 
00691     return TRUE;
00692 }
00693 
00694 static BOOL HLPFILE_RtfAddControl(struct RtfData* rd, const char* str)
00695 {
00696     if (*str == '\\' || *str == '{') rd->in_text = FALSE;
00697     else if (*str == '}') rd->in_text = TRUE;
00698     return HLPFILE_RtfAddRawString(rd, str, strlen(str));
00699 }
00700 
00701 static BOOL HLPFILE_RtfAddText(struct RtfData* rd, const char* str)
00702 {
00703     const char* p;
00704     const char* last;
00705     const char* replace;
00706     unsigned    rlen;
00707 
00708     if (!rd->in_text)
00709     {
00710         if (!HLPFILE_RtfAddRawString(rd, " ", 1)) return FALSE;
00711         rd->in_text = TRUE;
00712     }
00713     for (last = p = str; *p; p++)
00714     {
00715         if (*p < 0) /* escape non ASCII chars */
00716         {
00717             static char         xx[8];
00718             rlen = sprintf(xx, "\\'%x", *(const BYTE*)p);
00719             replace = xx;
00720         }
00721         else switch (*p)
00722         {
00723         case '{':  rlen = 2; replace = "\\{";  break;
00724         case '}':  rlen = 2; replace = "\\}";  break;
00725         case '\\': rlen = 2; replace = "\\\\"; break;
00726         default:   continue;
00727         }
00728         if ((p != last && !HLPFILE_RtfAddRawString(rd, last, p - last)) ||
00729             !HLPFILE_RtfAddRawString(rd, replace, rlen)) return FALSE;
00730         last = p + 1;
00731     }
00732     return HLPFILE_RtfAddRawString(rd, last, p - last);
00733 }
00734 
00735 /******************************************************************
00736  *      RtfAddHexBytes
00737  *
00738  */
00739 static BOOL HLPFILE_RtfAddHexBytes(struct RtfData* rd, const void* _ptr, unsigned sz)
00740 {
00741     char        tmp[512];
00742     unsigned    i, step;
00743     const BYTE* ptr = _ptr;
00744     static const char* _2hex = "0123456789abcdef";
00745 
00746     if (!rd->in_text)
00747     {
00748         if (!HLPFILE_RtfAddRawString(rd, " ", 1)) return FALSE;
00749         rd->in_text = TRUE;
00750     }
00751     for (; sz; sz -= step)
00752     {
00753         step = min(256, sz);
00754         for (i = 0; i < step; i++)
00755         {
00756             tmp[2 * i + 0] = _2hex[*ptr >> 4];
00757             tmp[2 * i + 1] = _2hex[*ptr++ & 0xF];
00758         }
00759         if (!HLPFILE_RtfAddRawString(rd, tmp, 2 * step)) return FALSE;
00760     }
00761     return TRUE;
00762 }
00763 
00764 static HLPFILE_LINK*       HLPFILE_AllocLink(struct RtfData* rd, int cookie,
00765                                              const char* str, unsigned len, LONG hash,
00766                                              unsigned clrChange, unsigned bHotSpot, unsigned wnd);
00767 
00768 /******************************************************************
00769  *      HLPFILE_AddHotSpotLinks
00770  *
00771  */
00772 static void HLPFILE_AddHotSpotLinks(struct RtfData* rd, HLPFILE* file,
00773                                     const BYTE* start, ULONG hs_size, ULONG hs_offset)
00774 {
00775     unsigned    i, hs_num;
00776     ULONG       hs_macro;
00777     const char* str;
00778 
00779     if (hs_size == 0 || hs_offset == 0) return;
00780 
00781     start += hs_offset;
00782     /* always 1 ?? */
00783     hs_num = GET_USHORT(start, 1);
00784     hs_macro = GET_UINT(start, 3);
00785 
00786     str = (const char*)start + 7 + 15 * hs_num + hs_macro;
00787     /* FIXME: should use hs_size to prevent out of bounds reads */
00788     for (i = 0; i < hs_num; i++)
00789     {
00790         HLPFILE_HOTSPOTLINK*    hslink;
00791 
00792         WINE_TRACE("%02x-%02x%02x {%s,%s}\n",
00793                    start[7 + 15 * i + 0], start[7 + 15 * i + 1], start[7 + 15 * i + 2],
00794                    str, str + strlen(str) + 1);
00795         /* str points to two null terminated strings:
00796          * hotspot name, then link name
00797          */
00798         str += strlen(str) + 1;     /* skip hotspot name */
00799 
00800         hslink = NULL;
00801         switch (start[7 + 15 * i + 0])
00802         /* The next two chars always look like 0x04 0x00 ???
00803          * What are they for ?
00804          */
00805         {
00806         case 0xC8:
00807             hslink = (HLPFILE_HOTSPOTLINK*)
00808                 HLPFILE_AllocLink(rd, hlp_link_macro, str, -1, 0, 0, 1, -1);
00809             break;
00810 
00811         case 0xE6:
00812         case 0xE7:
00813             hslink = (HLPFILE_HOTSPOTLINK*)
00814                 HLPFILE_AllocLink(rd, (start[7 + 15 * i + 0] & 1) ? hlp_link_link : hlp_link_popup,
00815                                   file->lpszPath, -1, HLPFILE_Hash(str),
00816                                   0, 1, -1);
00817             break;
00818 
00819         case 0xEE:
00820         case 0xEF:
00821             {
00822                 const char* win = strchr(str, '>');
00823                 int wnd = -1;
00824                 char* tgt = NULL;
00825 
00826                 if (win)
00827                 {
00828                     for (wnd = file->numWindows - 1; wnd >= 0; wnd--)
00829                     {
00830                         if (!strcmp(win + 1, file->windows[wnd].name)) break;
00831                     }
00832                     if (wnd == -1)
00833                         WINE_WARN("Couldn't find window info for %s\n", win);
00834                     if ((tgt = HeapAlloc(GetProcessHeap(), 0, win - str + 1)))
00835                     {
00836                         memcpy(tgt, str, win - str);
00837                         tgt[win - str] = '\0';
00838                     }
00839                 }
00840                 hslink = (HLPFILE_HOTSPOTLINK*)
00841                     HLPFILE_AllocLink(rd, (start[7 + 15 * i + 0] & 1) ? hlp_link_link : hlp_link_popup,
00842                                       file->lpszPath, -1, HLPFILE_Hash(tgt ? tgt : str), 0, 1, wnd);
00843                 HeapFree(GetProcessHeap(), 0, tgt);
00844                 break;
00845             }
00846         default:
00847             WINE_FIXME("unknown hotsport target 0x%x\n", start[7 + 15 * i + 0]);
00848         }
00849         if (hslink)
00850         {
00851             hslink->x      = GET_USHORT(start, 7 + 15 * i + 3);
00852             hslink->y      = GET_USHORT(start, 7 + 15 * i + 5);
00853             hslink->width  = GET_USHORT(start, 7 + 15 * i + 7);
00854             hslink->height = GET_USHORT(start, 7 + 15 * i + 9);
00855             /* target = GET_UINT(start, 7 + 15 * i + 11); */
00856         }
00857         str += strlen(str) + 1;
00858     }
00859 }
00860 
00861 /******************************************************************
00862  *             HLPFILE_RtfAddTransparentBitmap
00863  *
00864  * We'll transform a transparent bitmap into an metafile that
00865  * we then transform into RTF
00866  */
00867 static BOOL HLPFILE_RtfAddTransparentBitmap(struct RtfData* rd, const BITMAPINFO* bi,
00868                                             const void* pict, unsigned nc)
00869 {
00870     HDC                 hdc, hdcMask, hdcMem, hdcEMF;
00871     HBITMAP             hbm, hbmMask, hbmOldMask, hbmOldMem;
00872     HENHMETAFILE        hEMF;
00873     BOOL                ret = FALSE;
00874     void*               data;
00875     UINT                sz;
00876 
00877     hbm = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
00878                          CBM_INIT, pict, bi, DIB_RGB_COLORS);
00879 
00880     hdcMem = CreateCompatibleDC(hdc);
00881     hbmOldMem = SelectObject(hdcMem, hbm);
00882 
00883     /* create the mask bitmap from the main bitmap */
00884     hdcMask = CreateCompatibleDC(hdc);
00885     hbmMask = CreateBitmap(bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, 1, 1, NULL);
00886     hbmOldMask = SelectObject(hdcMask, hbmMask);
00887     SetBkColor(hdcMem,
00888                RGB(bi->bmiColors[nc - 1].rgbRed,
00889                    bi->bmiColors[nc - 1].rgbGreen,
00890                    bi->bmiColors[nc - 1].rgbBlue));
00891     BitBlt(hdcMask, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCCOPY);
00892 
00893     /* sets to RGB(0,0,0) the transparent bits in main bitmap */
00894     SetBkColor(hdcMem, RGB(0,0,0));
00895     SetTextColor(hdcMem, RGB(255,255,255));
00896     BitBlt(hdcMem, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMask, 0, 0, SRCAND);
00897 
00898     SelectObject(hdcMask, hbmOldMask);
00899     DeleteDC(hdcMask);
00900 
00901     SelectObject(hdcMem, hbmOldMem);
00902     DeleteDC(hdcMem);
00903 
00904     /* we create the bitmap on the fly */
00905     hdcEMF = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
00906     hdcMem = CreateCompatibleDC(hdcEMF);
00907 
00908     /* sets to RGB(0,0,0) the transparent bits in final bitmap */
00909     hbmOldMem = SelectObject(hdcMem, hbmMask);
00910     SetBkColor(hdcEMF, RGB(255, 255, 255));
00911     SetTextColor(hdcEMF, RGB(0, 0, 0));
00912     BitBlt(hdcEMF, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCAND);
00913 
00914     /* and copy the remaining bits of main bitmap */
00915     SelectObject(hdcMem, hbm);
00916     BitBlt(hdcEMF, 0, 0, bi->bmiHeader.biWidth, bi->bmiHeader.biHeight, hdcMem, 0, 0, SRCPAINT);
00917     SelectObject(hdcMem, hbmOldMem);
00918     DeleteDC(hdcMem);
00919 
00920     /* do the cleanup */
00921     ReleaseDC(0, hdc);
00922     DeleteObject(hbmMask);
00923     DeleteObject(hbm);
00924 
00925     hEMF = CloseEnhMetaFile(hdcEMF);
00926 
00927     /* generate rtf stream */
00928     sz = GetEnhMetaFileBits(hEMF, 0, NULL);
00929     if (sz && (data = HeapAlloc(GetProcessHeap(), 0, sz)))
00930     {
00931         if (sz == GetEnhMetaFileBits(hEMF, sz, data))
00932         {
00933             ret = HLPFILE_RtfAddControl(rd, "{\\pict\\emfblip") &&
00934                 HLPFILE_RtfAddHexBytes(rd, data, sz) &&
00935                 HLPFILE_RtfAddControl(rd, "}");
00936         }
00937         HeapFree(GetProcessHeap(), 0, data);
00938     }
00939     DeleteEnhMetaFile(hEMF);
00940 
00941     return ret;
00942 }
00943 
00944 /******************************************************************
00945  *      HLPFILE_RtfAddBitmap
00946  *
00947  */
00948 static BOOL HLPFILE_RtfAddBitmap(struct RtfData* rd, HLPFILE* file, const BYTE* beg, BYTE type, BYTE pack)
00949 {
00950     const BYTE*         ptr;
00951     const BYTE*         pict_beg;
00952     BYTE*               alloc = NULL;
00953     BITMAPINFO*         bi;
00954     ULONG               off, csz;
00955     unsigned            nc = 0;
00956     BOOL                clrImportant = FALSE;
00957     BOOL                ret = FALSE;
00958     char                tmp[256];
00959     unsigned            hs_size, hs_offset;
00960 
00961     bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
00962     if (!bi) return FALSE;
00963 
00964     ptr = beg + 2; /* for type and pack */
00965 
00966     bi->bmiHeader.biSize          = sizeof(bi->bmiHeader);
00967     bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
00968     bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
00969     bi->bmiHeader.biPlanes        = fetch_ushort(&ptr);
00970     bi->bmiHeader.biBitCount      = fetch_ushort(&ptr);
00971     bi->bmiHeader.biWidth         = fetch_ulong(&ptr);
00972     bi->bmiHeader.biHeight        = fetch_ulong(&ptr);
00973     bi->bmiHeader.biClrUsed       = fetch_ulong(&ptr);
00974     clrImportant  = fetch_ulong(&ptr);
00975     bi->bmiHeader.biClrImportant  = (clrImportant > 1) ? clrImportant : 0;
00976     bi->bmiHeader.biCompression   = BI_RGB;
00977     if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
00978     if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
00979     bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
00980     WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
00981                bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
00982                bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
00983 
00984     csz = fetch_ulong(&ptr);
00985     hs_size = fetch_ulong(&ptr);
00986 
00987     off = GET_UINT(ptr, 0); ptr += 4;
00988     hs_offset = GET_UINT(ptr, 0); ptr += 4;
00989     HLPFILE_AddHotSpotLinks(rd, file, beg, hs_size, hs_offset);
00990 
00991     /* now read palette info */
00992     if (type == 0x06)
00993     {
00994         unsigned i;
00995 
00996         nc = bi->bmiHeader.biClrUsed;
00997         /* not quite right, especially for bitfields type of compression */
00998         if (!nc && bi->bmiHeader.biBitCount <= 8)
00999             nc = 1 << bi->bmiHeader.biBitCount;
01000 
01001         bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
01002         if (!bi) return FALSE;
01003         for (i = 0; i < nc; i++)
01004         {
01005             bi->bmiColors[i].rgbBlue     = ptr[0];
01006             bi->bmiColors[i].rgbGreen    = ptr[1];
01007             bi->bmiColors[i].rgbRed      = ptr[2];
01008             bi->bmiColors[i].rgbReserved = 0;
01009             ptr += 4;
01010         }
01011     }
01012     pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack, &alloc);
01013 
01014     if (clrImportant == 1 && nc > 0)
01015     {
01016         ret = HLPFILE_RtfAddTransparentBitmap(rd, bi, pict_beg, nc);
01017         goto done;
01018     }
01019     if (!HLPFILE_RtfAddControl(rd, "{\\pict")) goto done;
01020     if (type == 0x06)
01021     {
01022         sprintf(tmp, "\\dibitmap0\\picw%d\\pich%d",
01023                 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
01024         if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01025         if (!HLPFILE_RtfAddHexBytes(rd, bi, sizeof(*bi) + nc * sizeof(RGBQUAD))) goto done;
01026     }
01027     else
01028     {
01029         sprintf(tmp, "\\wbitmap0\\wbmbitspixel%d\\wbmplanes%d\\picw%d\\pich%d",
01030                 bi->bmiHeader.biBitCount, bi->bmiHeader.biPlanes,
01031                 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
01032         if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01033     }
01034     if (!HLPFILE_RtfAddHexBytes(rd, pict_beg, bi->bmiHeader.biSizeImage)) goto done;
01035     if (!HLPFILE_RtfAddControl(rd, "}")) goto done;
01036 
01037     ret = TRUE;
01038 done:
01039     HeapFree(GetProcessHeap(), 0, bi);
01040     HeapFree(GetProcessHeap(), 0, alloc);
01041 
01042     return ret;
01043 }
01044 
01045 /******************************************************************
01046  *      HLPFILE_RtfAddMetaFile
01047  *
01048  */
01049 static BOOL     HLPFILE_RtfAddMetaFile(struct RtfData* rd, HLPFILE* file, const BYTE* beg, BYTE pack)
01050 {
01051     ULONG               size, csize, off, hs_offset, hs_size;
01052     const BYTE*         ptr;
01053     const BYTE*         bits;
01054     BYTE*               alloc = NULL;
01055     char                tmp[256];
01056     unsigned            mm;
01057     BOOL                ret;
01058 
01059     WINE_TRACE("Loading metafile\n");
01060 
01061     ptr = beg + 2; /* for type and pack */
01062 
01063     mm = fetch_ushort(&ptr); /* mapping mode */
01064     sprintf(tmp, "{\\pict\\wmetafile%d\\picw%d\\pich%d",
01065             mm, GET_USHORT(ptr, 0), GET_USHORT(ptr, 2));
01066     if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE;
01067     ptr += 4;
01068 
01069     size = fetch_ulong(&ptr); /* decompressed size */
01070     csize = fetch_ulong(&ptr); /* compressed size */
01071     hs_size = fetch_ulong(&ptr); /* hotspot size */
01072     off = GET_UINT(ptr, 0);
01073     hs_offset = GET_UINT(ptr, 4);
01074     ptr += 8;
01075 
01076     HLPFILE_AddHotSpotLinks(rd, file, beg, hs_size, hs_offset);
01077 
01078     WINE_TRACE("sz=%u csz=%u offs=%u/%u,%u/%u\n",
01079                size, csize, off, (ULONG)(ptr - beg), hs_size, hs_offset);
01080 
01081     bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack, &alloc);
01082     if (!bits) return FALSE;
01083 
01084     ret = HLPFILE_RtfAddHexBytes(rd, bits, size) &&
01085         HLPFILE_RtfAddControl(rd, "}");
01086 
01087     HeapFree(GetProcessHeap(), 0, alloc);
01088 
01089     return ret;
01090 }
01091 
01092 /******************************************************************
01093  *      HLPFILE_RtfAddGfxByAddr
01094  *
01095  */
01096 static  BOOL    HLPFILE_RtfAddGfxByAddr(struct RtfData* rd, HLPFILE *hlpfile,
01097                                         const BYTE* ref, ULONG size)
01098 {
01099     unsigned    i, numpict;
01100 
01101     numpict = GET_USHORT(ref, 2);
01102     WINE_TRACE("Got picture magic=%04x #=%d\n", GET_USHORT(ref, 0), numpict);
01103 
01104     for (i = 0; i < numpict; i++)
01105     {
01106         const BYTE*     beg;
01107         const BYTE*     ptr;
01108         BYTE            type, pack;
01109 
01110         WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
01111         beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
01112 
01113         type = *ptr++;
01114         pack = *ptr++;
01115 
01116         switch (type)
01117         {
01118         case 5: /* device dependent bmp */
01119         case 6: /* device independent bmp */
01120             HLPFILE_RtfAddBitmap(rd, hlpfile, beg, type, pack);
01121             break;
01122         case 8:
01123             HLPFILE_RtfAddMetaFile(rd, hlpfile, beg, pack);
01124             break;
01125         default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
01126         }
01127 
01128         /* FIXME: hotspots */
01129 
01130         /* FIXME: implement support for multiple picture format */
01131         if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
01132         break;
01133     }
01134     return TRUE;
01135 }
01136 
01137 /******************************************************************
01138  *      HLPFILE_RtfAddGfxByIndex
01139  *
01140  *
01141  */
01142 static  BOOL    HLPFILE_RtfAddGfxByIndex(struct RtfData* rd, HLPFILE *hlpfile,
01143                                          unsigned index)
01144 {
01145     char        tmp[16];
01146     BYTE        *ref, *end;
01147 
01148     WINE_TRACE("Loading picture #%d\n", index);
01149 
01150     sprintf(tmp, "|bm%u", index);
01151 
01152     if (!HLPFILE_FindSubFile(hlpfile, tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
01153 
01154     ref += 9;
01155     return HLPFILE_RtfAddGfxByAddr(rd, hlpfile, ref, end - ref);
01156 }
01157 
01158 /******************************************************************
01159  *      HLPFILE_AllocLink
01160  *
01161  *
01162  */
01163 static HLPFILE_LINK*       HLPFILE_AllocLink(struct RtfData* rd, int cookie,
01164                                              const char* str, unsigned len, LONG hash,
01165                                              unsigned clrChange, unsigned bHotSpot, unsigned wnd)
01166 {
01167     HLPFILE_LINK*  link;
01168     char*          link_str;
01169     unsigned       asz = bHotSpot ? sizeof(HLPFILE_HOTSPOTLINK) : sizeof(HLPFILE_LINK);
01170 
01171     /* FIXME: should build a string table for the attributes.link.lpszPath
01172      * they are reallocated for each link
01173      */
01174     if (len == -1) len = strlen(str);
01175     link = HeapAlloc(GetProcessHeap(), 0, asz + len + 1);
01176     if (!link) return NULL;
01177 
01178     link->cookie     = cookie;
01179     link->string     = link_str = (char*)link + asz;
01180     memcpy(link_str, str, len);
01181     link_str[len] = '\0';
01182     link->hash       = hash;
01183     link->bClrChange = clrChange ? 1 : 0;
01184     link->bHotSpot   = bHotSpot;
01185     link->window     = wnd;
01186     link->next       = rd->first_link;
01187     rd->first_link   = link;
01188     link->cpMin      = rd->char_pos;
01189     rd->force_color  = clrChange;
01190     if (rd->current_link) WINE_FIXME("Pending link\n");
01191     if (bHotSpot)
01192         link->cpMax = rd->char_pos;
01193     else
01194         rd->current_link = link;
01195 
01196     WINE_TRACE("Link[%d] to %s@%08x:%d\n",
01197                link->cookie, link->string, link->hash, link->window);
01198     return link;
01199 }
01200 
01201 static unsigned HLPFILE_HalfPointsToTwips(unsigned pts)
01202 {
01203     static unsigned logPxY;
01204     if (!logPxY)
01205     {
01206         HDC hdc = GetDC(NULL);
01207         logPxY = GetDeviceCaps(hdc, LOGPIXELSY);
01208         ReleaseDC(NULL, hdc);
01209     }
01210     return MulDiv(pts, 72 * 10, logPxY);
01211 }
01212 
01213 /***********************************************************************
01214  *
01215  *           HLPFILE_BrowseParagraph
01216  */
01217 static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, struct RtfData* rd,
01218                                     BYTE *buf, BYTE* end, unsigned* parlen)
01219 {
01220     UINT               textsize;
01221     const BYTE        *format, *format_end;
01222     char              *text, *text_base, *text_end;
01223     LONG               size, blocksize, datalen;
01224     unsigned short     bits;
01225     unsigned           nc, ncol = 1;
01226     short              table_width;
01227     BOOL               in_table = FALSE;
01228     char               tmp[256];
01229     BOOL               ret = FALSE;
01230 
01231     if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
01232 
01233     *parlen = 0;
01234     blocksize = GET_UINT(buf, 0);
01235     size = GET_UINT(buf, 0x4);
01236     datalen = GET_UINT(buf, 0x10);
01237     text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
01238     if (!text) return FALSE;
01239     if (size > blocksize - datalen)
01240     {
01241         /* need to decompress */
01242         if (page->file->hasPhrases)
01243             HLPFILE_Uncompress2(page->file, buf + datalen, end, (BYTE*)text, (BYTE*)text + size);
01244         else if (page->file->hasPhrases40)
01245             HLPFILE_Uncompress3(page->file, text, text + size, buf + datalen, end);
01246         else
01247         {
01248             WINE_FIXME("Text size is too long, splitting\n");
01249             size = blocksize - datalen;
01250             memcpy(text, buf + datalen, size);
01251         }
01252     }
01253     else
01254         memcpy(text, buf + datalen, size);
01255 
01256     text_end = text + size;
01257 
01258     format = buf + 0x15;
01259     format_end = buf + GET_UINT(buf, 0x10);
01260 
01261     if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
01262     {
01263         fetch_long(&format);
01264         *parlen = fetch_ushort(&format);
01265     }
01266 
01267     if (buf[0x14] == 0x23)
01268     {
01269         char    type;
01270 
01271         in_table = TRUE;
01272         ncol = *format++;
01273 
01274         if (!HLPFILE_RtfAddControl(rd, "\\trowd")) goto done;
01275         type = *format++;
01276         if (type == 0 || type == 2)
01277         {
01278             table_width = GET_SHORT(format, 0);
01279             format += 2;
01280         }
01281         else
01282             table_width = 32767;
01283         WINE_TRACE("New table: cols=%d type=%x width=%d\n",
01284                    ncol, type, table_width);
01285         if (ncol > 1)
01286         {
01287             int     pos;
01288             sprintf(tmp, "\\trgaph%d\\trleft%d",
01289                     HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format, 6), table_width, 32767)),
01290                     HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format, 0), table_width, 32767)));
01291             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01292             pos = HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format, 6) / 2, table_width, 32767));
01293             for (nc = 0; nc < ncol; nc++)
01294             {
01295                 WINE_TRACE("column(%d/%d) gap=%d width=%d\n",
01296                            nc, ncol, GET_SHORT(format, nc*4),
01297                            GET_SHORT(format, nc*4+2));
01298                 pos += GET_SHORT(format, nc * 4) + GET_SHORT(format, nc * 4 + 2);
01299                 sprintf(tmp, "\\cellx%d",
01300                         HLPFILE_HalfPointsToTwips(MulDiv(pos, table_width, 32767)));
01301                 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01302             }
01303         }
01304         else
01305         {
01306             WINE_TRACE("column(0/%d) gap=%d width=%d\n",
01307                        ncol, GET_SHORT(format, 0), GET_SHORT(format, 2));
01308             sprintf(tmp, "\\trleft%d\\cellx%d ",
01309                     HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format, 0), table_width, 32767)),
01310                     HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format, 0) + GET_SHORT(format, 2),
01311                                       table_width, 32767)));
01312             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01313         }
01314         format += ncol * 4;
01315     }
01316 
01317     for (nc = 0; nc < ncol; )
01318     {
01319         WINE_TRACE("looking for format at offset %lu in column %d\n", (SIZE_T)(format - (buf + 0x15)), nc);
01320         if (!HLPFILE_RtfAddControl(rd, "\\pard")) goto done;
01321         if (in_table)
01322         {
01323             nc = GET_SHORT(format, 0);
01324             if (nc == -1) break;
01325             format += 5;
01326             if (!HLPFILE_RtfAddControl(rd, "\\intbl")) goto done;
01327         }
01328         else nc++;
01329         if (buf[0x14] == 0x01)
01330             format += 6;
01331         else
01332             format += 4;
01333         bits = GET_USHORT(format, 0); format += 2;
01334         if (bits & 0x0001) fetch_long(&format);
01335         if (bits & 0x0002)
01336         {
01337             sprintf(tmp, "\\sb%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01338             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01339         }
01340         if (bits & 0x0004)
01341         {
01342             sprintf(tmp, "\\sa%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01343             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01344         }
01345         if (bits & 0x0008)
01346         {
01347             sprintf(tmp, "\\sl%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01348             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01349         }
01350         if (bits & 0x0010)
01351         {
01352             sprintf(tmp, "\\li%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01353             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01354         }
01355         if (bits & 0x0020)
01356         {
01357             sprintf(tmp, "\\ri%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01358             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01359         }
01360         if (bits & 0x0040)
01361         {
01362             sprintf(tmp, "\\fi%d", HLPFILE_HalfPointsToTwips(fetch_short(&format)));
01363             if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01364         }
01365         if (bits & 0x0100)
01366         {
01367             BYTE        brdr = *format++;
01368             short       w;
01369 
01370             if (brdr & 0x01 && !HLPFILE_RtfAddControl(rd, "\\box")) goto done;
01371             if (brdr & 0x02 && !HLPFILE_RtfAddControl(rd, "\\brdrt")) goto done;
01372             if (brdr & 0x04 && !HLPFILE_RtfAddControl(rd, "\\brdrl")) goto done;
01373             if (brdr & 0x08 && !HLPFILE_RtfAddControl(rd, "\\brdrb")) goto done;
01374             if (brdr & 0x10 && !HLPFILE_RtfAddControl(rd, "\\brdrr")) goto done;
01375             if (brdr & 0x20 && !HLPFILE_RtfAddControl(rd, "\\brdrth")) goto done;
01376             if (!(brdr & 0x20) && !HLPFILE_RtfAddControl(rd, "\\brdrs")) goto done;
01377             if (brdr & 0x40 && !HLPFILE_RtfAddControl(rd, "\\brdrdb")) goto done;
01378             /* 0x80: unknown */
01379 
01380             w = GET_SHORT(format, 0); format += 2;
01381             if (w)
01382             {
01383                 sprintf(tmp, "\\brdrw%d", HLPFILE_HalfPointsToTwips(w));
01384                 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01385             }
01386         }
01387         if (bits & 0x0200)
01388         {
01389             int                 i, ntab = fetch_short(&format);
01390             unsigned            tab, ts;
01391             const char*         kind;
01392 
01393             for (i = 0; i < ntab; i++)
01394             {
01395                 tab = fetch_ushort(&format);
01396                 ts = (tab & 0x4000) ? fetch_ushort(&format) : 0 /* left */;
01397                 switch (ts)
01398                 {
01399                 default: WINE_FIXME("Unknown tab style %x\n", ts);
01400                 /* fall through */
01401                 case 0: kind = ""; break;
01402                 case 1: kind = "\\tqr"; break;
01403                 case 2: kind = "\\tqc"; break;
01404                 }
01405                 /* FIXME: do kind */
01406                 sprintf(tmp, "%s\\tx%d",
01407                         kind, HLPFILE_HalfPointsToTwips(tab & 0x3FFF));
01408                 if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01409             }
01410         }
01411         switch (bits & 0xc00)
01412         {
01413         default: WINE_FIXME("Unsupported alignment 0xC00\n"); break;
01414         case 0: if (!HLPFILE_RtfAddControl(rd, "\\ql")) goto done; break;
01415         case 0x400: if (!HLPFILE_RtfAddControl(rd, "\\qr")) goto done; break;
01416         case 0x800: if (!HLPFILE_RtfAddControl(rd, "\\qc")) goto done; break;
01417         }
01418 
01419         /* 0x1000 doesn't need space */
01420         if ((bits & 0x1000) && !HLPFILE_RtfAddControl(rd, "\\keep")) goto done;
01421         if ((bits & 0xE080) != 0) 
01422             WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
01423 
01424         while (text < text_end && format < format_end)
01425         {
01426             WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
01427             textsize = strlen(text);
01428             if (textsize)
01429             {
01430                 if (rd->force_color)
01431                 {
01432                     if ((rd->current_link->cookie == hlp_link_popup) ?
01433                         !HLPFILE_RtfAddControl(rd, "{\\uld\\cf1") :
01434                         !HLPFILE_RtfAddControl(rd, "{\\ul\\cf1")) goto done;
01435                 }
01436                 if (!HLPFILE_RtfAddText(rd, text)) goto done;
01437                 if (rd->force_color && !HLPFILE_RtfAddControl(rd, "}")) goto done;
01438                 rd->char_pos += textsize;
01439             }
01440             /* else: null text, keep on storing attributes */
01441             text += textsize + 1;
01442 
01443         if (*format == 0xff)
01444             {
01445                 format++;
01446                 break;
01447             }
01448 
01449             WINE_TRACE("format=%02x\n", *format);
01450             switch (*format)
01451             {
01452             case 0x20:
01453                 WINE_FIXME("NIY20\n");
01454                 format += 5;
01455                 break;
01456 
01457             case 0x21:
01458                 WINE_FIXME("NIY21\n");
01459                 format += 3;
01460                 break;
01461 
01462         case 0x80:
01463                 {
01464                     unsigned    font = GET_USHORT(format, 1);
01465                     unsigned    fs;
01466 
01467                     WINE_TRACE("Changing font to %d\n", font);
01468                     format += 3;
01469                     /* Font size in hlpfile is given in the same units as
01470                        rtf control word \fs uses (half-points). */
01471                     switch (rd->font_scale)
01472                     {
01473                     case 0: fs = page->file->fonts[font].LogFont.lfHeight - 4; break;
01474                     default:
01475                     case 1: fs = page->file->fonts[font].LogFont.lfHeight; break;
01476                     case 2: fs = page->file->fonts[font].LogFont.lfHeight + 4; break;
01477                     }
01478                     /* FIXME: missing at least colors, also bold attribute looses information */
01479 
01480                     sprintf(tmp, "\\f%d\\cf%d\\fs%d%s%s%s%s",
01481                             font, font + 2, fs,
01482                             page->file->fonts[font].LogFont.lfWeight > 400 ? "\\b" : "\\b0",
01483                             page->file->fonts[font].LogFont.lfItalic ? "\\i" : "\\i0",
01484                             page->file->fonts[font].LogFont.lfUnderline ? "\\ul" : "\\ul0",
01485                             page->file->fonts[font].LogFont.lfStrikeOut ? "\\strike" : "\\strike0");
01486                     if (!HLPFILE_RtfAddControl(rd, tmp)) goto done;
01487                 }
01488                break;
01489 
01490         case 0x81:
01491                 if (!HLPFILE_RtfAddControl(rd, "\\line")) goto done;
01492                 format += 1;
01493                 rd->char_pos++;
01494                 break;
01495 
01496         case 0x82:
01497                 if (in_table)
01498                 {
01499                     if (format[1] != 0xFF)
01500                     {
01501                         if (!HLPFILE_RtfAddControl(rd, "\\par\\intbl")) goto done;
01502                     }
01503                     else
01504                     {
01505                         if (!HLPFILE_RtfAddControl(rd, "\\cell\\pard\\intbl")) goto done;
01506                     }
01507                 }
01508                 else if (!HLPFILE_RtfAddControl(rd, "\\par")) goto done;
01509                 format += 1;
01510                 rd->char_pos++;
01511                 break;
01512 
01513         case 0x83:
01514                 if (!HLPFILE_RtfAddControl(rd, "\\tab")) goto done;
01515                 format += 1;
01516                 rd->char_pos++;
01517                 break;
01518 
01519 #if 0
01520         case 0x84:
01521                 format += 3;
01522                 break;
01523 #endif
01524 
01525         case 0x86:
01526         case 0x87:
01527         case 0x88:
01528                 {
01529                     BYTE    type = format[1];
01530                     LONG    size;
01531 
01532                     /* FIXME: we don't use 'BYTE    pos = (*format - 0x86);' for the image position */
01533                     format += 2;
01534                     size = fetch_long(&format);
01535 
01536                     switch (type)
01537                     {
01538                     case 0x22:
01539                         fetch_ushort(&format); /* hot spot */
01540                         /* fall thru */
01541                     case 0x03:
01542                         switch (GET_SHORT(format, 0))
01543                         {
01544                         case 0:
01545                             HLPFILE_RtfAddGfxByIndex(rd, page->file, GET_SHORT(format, 2));
01546                             rd->char_pos++;
01547                             break;
01548                         case 1:
01549                             WINE_FIXME("does it work ??? %x<%u>#%u\n",
01550                                        GET_SHORT(format, 0),
01551                                        size, GET_SHORT(format, 2));
01552                             HLPFILE_RtfAddGfxByAddr(rd, page->file, format + 2, size - 4);
01553                             rd->char_pos++;
01554                            break;
01555                         default:
01556                             WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
01557                             break;
01558                         }
01559                         break;
01560                     case 0x05:
01561                         WINE_FIXME("Got an embedded element %s\n", format + 6);
01562                         break;
01563                     default:
01564                         WINE_FIXME("Got a type %d picture\n", type);
01565                         break;
01566                     }
01567                     format += size;
01568                 }
01569                 break;
01570 
01571         case 0x89:
01572                 format += 1;
01573                 if (!rd->current_link)
01574                     WINE_FIXME("No existing link\n");
01575                 rd->current_link->cpMax = rd->char_pos;
01576                 rd->current_link = NULL;
01577                 rd->force_color = FALSE;
01578                 break;
01579 
01580             case 0x8B:
01581                 if (!HLPFILE_RtfAddControl(rd, "\\~")) goto done;
01582                 format += 1;
01583                 rd->char_pos++;
01584                 break;
01585 
01586             case 0x8C:
01587                 if (!HLPFILE_RtfAddControl(rd, "\\_")) goto done;
01588                 /* FIXME: it could be that hypen is also in input stream !! */
01589                 format += 1;
01590                 rd->char_pos++;
01591                 break;
01592 
01593 #if 0
01594         case 0xA9:
01595                 format += 2;
01596                 break;
01597 #endif
01598 
01599             case 0xC8:
01600             case 0xCC:
01601                 WINE_TRACE("macro => %s\n", format + 3);
01602                 HLPFILE_AllocLink(rd, hlp_link_macro, (const char*)format + 3,
01603                                   GET_USHORT(format, 1), 0, !(*format & 4), 0, -1);
01604                 format += 3 + GET_USHORT(format, 1);
01605                 break;
01606 
01607             case 0xE0:
01608             case 0xE1:
01609                 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
01610                 HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup,
01611                                   page->file->lpszPath, -1, GET_UINT(format, 1), 1, 0, -1);
01612 
01613 
01614                 format += 5;
01615                 break;
01616 
01617         case 0xE2:
01618         case 0xE3:
01619             case 0xE6:
01620             case 0xE7:
01621                 HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup,
01622                                   page->file->lpszPath, -1, GET_UINT(format, 1),
01623                                   !(*format & 4), 0, -1);
01624                 format += 5;
01625                 break;
01626 
01627         case 0xEA:
01628             case 0xEB:
01629             case 0xEE:
01630             case 0xEF:
01631                 {
01632                     const char*       ptr = (const char*) format + 8;
01633                     BYTE        type = format[3];
01634                     int         wnd = -1;
01635 
01636                     switch (type)
01637                     {
01638                     case 1:
01639                         wnd = *ptr;
01640                         /* fall through */
01641                     case 0:
01642                         ptr = page->file->lpszPath;
01643                         break;
01644                     case 6:
01645                         for (wnd = page->file->numWindows - 1; wnd >= 0; wnd--)
01646                         {
01647                             if (!strcmp(ptr, page->file->windows[wnd].name)) break;
01648                         }
01649                         if (wnd == -1)
01650                             WINE_WARN("Couldn't find window info for %s\n", ptr);
01651                         ptr += strlen(ptr) + 1;
01652                         /* fall through */
01653                     case 4:
01654                         break;
01655                     default:
01656                         WINE_WARN("Unknown link type %d\n", type);
01657                         break;
01658                     }
01659                     HLPFILE_AllocLink(rd, (*format & 1) ? hlp_link_link : hlp_link_popup,
01660                                       ptr, -1, GET_UINT(format, 4), !(*format & 4), 0, wnd);
01661                 }
01662                 format += 3 + GET_USHORT(format, 1);
01663                 break;
01664 
01665         default:
01666                 WINE_WARN("format %02x\n", *format);
01667                 format++;
01668         }
01669     }
01670     }
01671     if (in_table)
01672     {
01673         if (!HLPFILE_RtfAddControl(rd, "\\row\\par\\pard\\plain")) goto done;
01674         rd->char_pos += 2;
01675     }
01676     ret = TRUE;
01677 done:
01678 
01679     HeapFree(GetProcessHeap(), 0, text_base);
01680     return ret;
01681 }
01682 
01683 /******************************************************************
01684  *      HLPFILE_BrowsePage
01685  *
01686  */
01687 BOOL    HLPFILE_BrowsePage(HLPFILE_PAGE* page, struct RtfData* rd,
01688                            unsigned font_scale, unsigned relative)
01689 {
01690     HLPFILE     *hlpfile = page->file;
01691     BYTE        *buf, *end;
01692     DWORD       ref = page->reference;
01693     unsigned    index, old_index = -1, offset, count = 0, offs = 0;
01694     unsigned    cpg, parlen;
01695     char        tmp[1024];
01696     const char* ck = NULL;
01697 
01698     rd->in_text = TRUE;
01699     rd->data = rd->ptr = HeapAlloc(GetProcessHeap(), 0, rd->allocated = 32768);
01700     rd->char_pos = 0;
01701     rd->first_link = rd->current_link = NULL;
01702     rd->force_color = FALSE;
01703     rd->font_scale = font_scale;
01704     rd->relative = relative;
01705     rd->char_pos_rel = 0;
01706 
01707     switch (hlpfile->charset)
01708     {
01709     case DEFAULT_CHARSET:
01710     case ANSI_CHARSET:          cpg = 1252; break;
01711     case SHIFTJIS_CHARSET:      cpg = 932; break;
01712     case HANGEUL_CHARSET:       cpg = 949; break;
01713     case GB2312_CHARSET:        cpg = 936; break;
01714     case CHINESEBIG5_CHARSET:   cpg = 950; break;
01715     case GREEK_CHARSET:         cpg = 1253; break;
01716     case TURKISH_CHARSET:       cpg = 1254; break;
01717     case HEBREW_CHARSET:        cpg = 1255; break;
01718     case ARABIC_CHARSET:        cpg = 1256; break;
01719     case BALTIC_CHARSET:        cpg = 1257; break;
01720     case VIETNAMESE_CHARSET:    cpg = 1258; break;
01721     case RUSSIAN_CHARSET:       cpg = 1251; break;
01722     case EE_CHARSET:            cpg = 1250; break;
01723     case THAI_CHARSET:          cpg = 874; break;
01724     case JOHAB_CHARSET:         cpg = 1361; break;
01725     case MAC_CHARSET:           ck = "mac"; break;
01726     default:
01727         WINE_FIXME("Unsupported charset %u\n", hlpfile->charset);
01728         cpg = 1252;
01729     }
01730     if (ck)
01731     {
01732         sprintf(tmp, "{\\rtf1\\%s\\deff0", ck);
01733         if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE;
01734     }
01735     else
01736     {
01737         sprintf(tmp, "{\\rtf1\\ansi\\ansicpg%d\\deff0", cpg);
01738         if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE;
01739     }
01740 
01741     /* generate font table */
01742     if (!HLPFILE_RtfAddControl(rd, "{\\fonttbl")) return FALSE;
01743     for (index = 0; index < hlpfile->numFonts; index++)
01744     {
01745         const char* family;
01746         switch (hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0xF0)
01747         {
01748         case FF_MODERN:     family = "modern";  break;
01749         case FF_ROMAN:      family = "roman";   break;
01750         case FF_SWISS:      family = "swiss";   break;
01751         case FF_SCRIPT:     family = "script";  break;
01752         case FF_DECORATIVE: family = "decor";   break;
01753         default:            family = "nil";     break;
01754         }
01755         sprintf(tmp, "{\\f%d\\f%s\\fprq%d\\fcharset%d %s;}",
01756                 index, family,
01757                 hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0x0F,
01758                 hlpfile->fonts[index].LogFont.lfCharSet,
01759                 hlpfile->fonts[index].LogFont.lfFaceName);
01760         if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE;
01761     }
01762     if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE;
01763     /* generate color table */
01764     if (!HLPFILE_RtfAddControl(rd, "{\\colortbl ;\\red0\\green128\\blue0;")) return FALSE;
01765     for (index = 0; index < hlpfile->numFonts; index++)
01766     {
01767         sprintf(tmp, "\\red%d\\green%d\\blue%d;",
01768                 GetRValue(hlpfile->fonts[index].color),
01769                 GetGValue(hlpfile->fonts[index].color),
01770                 GetBValue(hlpfile->fonts[index].color));
01771         if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE;
01772     }
01773     if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE;
01774 
01775     do
01776     {
01777         if (hlpfile->version <= 16)
01778         {
01779             index  = (ref - 0x0C) / hlpfile->dsize;
01780             offset = (ref - 0x0C) % hlpfile->dsize;
01781         }
01782         else
01783         {
01784             index  = (ref - 0x0C) >> 14;
01785             offset = (ref - 0x0C) & 0x3FFF;
01786         }
01787 
01788         if (hlpfile->version <= 16 && index != old_index && old_index != -1)
01789         {
01790             /* we jumped to the next block, adjust pointers */
01791             ref -= 12;
01792             offset -= 12;
01793         }
01794 
01795         if (index >= hlpfile->topic_maplen) {WINE_WARN("maplen\n"); break;}
01796         buf = hlpfile->topic_map[index] + offset;
01797         if (buf + 0x15 >= hlpfile->topic_end) {WINE_WARN("extra\n"); break;}
01798         end = min(buf + GET_UINT(buf, 0), hlpfile->topic_end);
01799         if (index != old_index) {offs = 0; old_index = index;}
01800 
01801         switch (buf[0x14])
01802         {
01803         case 0x02:
01804             if (count++) goto done;
01805             break;
01806         case 0x01:
01807         case 0x20:
01808         case 0x23:
01809             if (!HLPFILE_BrowseParagraph(page, rd, buf, end, &parlen)) return FALSE;
01810             if (relative > index * 0x8000 + offs)
01811                 rd->char_pos_rel = rd->char_pos;
01812             offs += parlen;
01813             break;
01814         default:
01815             WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
01816         }
01817         if (hlpfile->version <= 16)
01818         {
01819             ref += GET_UINT(buf, 0xc);
01820             if (GET_UINT(buf, 0xc) == 0)
01821                 break;
01822         }
01823         else
01824             ref = GET_UINT(buf, 0xc);
01825     } while (ref != 0xffffffff);
01826 done:
01827     page->first_link = rd->first_link;
01828     return HLPFILE_RtfAddControl(rd, "}");
01829 }
01830 
01831 /******************************************************************
01832  *      HLPFILE_ReadFont
01833  *
01834  *
01835  */
01836 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
01837 {
01838     BYTE        *ref, *end;
01839     unsigned    i, len, idx;
01840     unsigned    face_num, dscr_num, face_offset, dscr_offset;
01841     BYTE        flag, family;
01842 
01843     if (!HLPFILE_FindSubFile(hlpfile, "|FONT", &ref, &end))
01844     {
01845         WINE_WARN("no subfile FONT\n");
01846         hlpfile->numFonts = 0;
01847         hlpfile->fonts = NULL;
01848         return FALSE;
01849     }
01850 
01851     ref += 9;
01852 
01853     face_num    = GET_USHORT(ref, 0);
01854     dscr_num    = GET_USHORT(ref, 2);
01855     face_offset = GET_USHORT(ref, 4);
01856     dscr_offset = GET_USHORT(ref, 6);
01857 
01858     WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
01859                face_num, face_offset, dscr_num, dscr_offset);
01860 
01861     hlpfile->numFonts = dscr_num;
01862     hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
01863 
01864     len = (dscr_offset - face_offset) / face_num;
01865 /* EPP     for (i = face_offset; i < dscr_offset; i += len) */
01866 /* EPP         WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
01867     for (i = 0; i < dscr_num; i++)
01868     {
01869         flag = ref[dscr_offset + i * 11 + 0];
01870         family = ref[dscr_offset + i * 11 + 2];
01871 
01872         hlpfile->fonts[i].LogFont.lfHeight = ref[dscr_offset + i * 11 + 1];
01873         hlpfile->fonts[i].LogFont.lfWidth = 0;
01874         hlpfile->fonts[i].LogFont.lfEscapement = 0;
01875         hlpfile->fonts[i].LogFont.lfOrientation = 0;
01876         hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
01877         hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
01878         hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
01879         hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
01880         hlpfile->fonts[i].LogFont.lfCharSet = hlpfile->charset;
01881         hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
01882         hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
01883         hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
01884         hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
01885 
01886         switch (family)
01887         {
01888         case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN;     break;
01889         case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN;      break;
01890         case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS;      break;
01891         case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT;     break;
01892         case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
01893         default: WINE_FIXME("Unknown family %u\n", family);
01894         }
01895         idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
01896 
01897         if (idx < face_num)
01898         {
01899             memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
01900             hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
01901         }
01902         else
01903         {
01904             WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
01905             strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
01906         }
01907         hlpfile->fonts[i].hFont = 0;
01908         hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
01909                                       ref[dscr_offset + i * 11 + 6],
01910                                       ref[dscr_offset + i * 11 + 7]);
01911 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
01912         WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
01913                    i, flag,
01914                    X(0, "bold"),
01915                    X(1, "italic"),
01916                    X(2, "underline"),
01917                    X(3, "strikeOut"),
01918                    X(4, "dblUnderline"),
01919                    X(5, "smallCaps"),
01920                    ref[dscr_offset + i * 11 + 1],
01921                    family,
01922                    hlpfile->fonts[i].LogFont.lfFaceName, idx,
01923                    GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
01924     }
01925     return TRUE;
01926 }
01927 
01928 /***********************************************************************
01929  *
01930  *           HLPFILE_ReadFileToBuffer
01931  */
01932 static BOOL HLPFILE_ReadFileToBuffer(HLPFILE* hlpfile, HFILE hFile)
01933 {
01934     BYTE  header[16], dummy[1];
01935 
01936     if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
01937 
01938     /* sanity checks */
01939     if (GET_UINT(header, 0) != 0x00035F3F)
01940     {WINE_WARN("wrong header\n"); return FALSE;};
01941 
01942     hlpfile->file_buffer_size = GET_UINT(header, 12);
01943     hlpfile->file_buffer = HeapAlloc(GetProcessHeap(), 0, hlpfile->file_buffer_size + 1);
01944     if (!hlpfile->file_buffer) return FALSE;
01945 
01946     memcpy(hlpfile->file_buffer, header, 16);
01947     if (_hread(hFile, hlpfile->file_buffer + 16, hlpfile->file_buffer_size - 16) !=hlpfile->file_buffer_size - 16)
01948     {WINE_WARN("filesize1\n"); return FALSE;};
01949 
01950     if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
01951 
01952     hlpfile->file_buffer[hlpfile->file_buffer_size] = '\0'; /* FIXME: was '0', sounds backwards to me */
01953 
01954     return TRUE;
01955 }
01956 
01957 /***********************************************************************
01958  *
01959  *           HLPFILE_SystemCommands
01960  */
01961 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
01962 {
01963     BYTE *buf, *ptr, *end;
01964     HLPFILE_MACRO *macro, **m;
01965     LPSTR p;
01966     unsigned short magic, minor, major, flags;
01967 
01968     hlpfile->lpszTitle = NULL;
01969 
01970     if (!HLPFILE_FindSubFile(hlpfile, "|SYSTEM", &buf, &end)) return FALSE;
01971 
01972     magic = GET_USHORT(buf + 9, 0);
01973     minor = GET_USHORT(buf + 9, 2);
01974     major = GET_USHORT(buf + 9, 4);
01975     /* gen date on 4 bytes */
01976     flags = GET_USHORT(buf + 9, 10);
01977     WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
01978                magic, major, minor, flags);
01979     if (magic != 0x036C || major != 1)
01980     {WINE_WARN("Wrong system header\n"); return FALSE;}
01981     if (minor <= 16)
01982     {
01983         hlpfile->tbsize = 0x800;
01984         hlpfile->compressed = 0;
01985     }
01986     else if (flags == 0)
01987     {
01988         hlpfile->tbsize = 0x1000;
01989         hlpfile->compressed = 0;
01990     }
01991     else if (flags == 4)
01992     {
01993         hlpfile->tbsize = 0x1000;
01994         hlpfile->compressed = 1;
01995     }
01996     else
01997     {
01998         hlpfile->tbsize = 0x800;
01999         hlpfile->compressed = 1;
02000     }
02001 
02002     if (hlpfile->compressed)
02003         hlpfile->dsize = 0x4000;
02004     else
02005         hlpfile->dsize = hlpfile->tbsize - 0x0C;
02006 
02007     hlpfile->version = minor;
02008     hlpfile->flags = flags;
02009     hlpfile->charset = DEFAULT_CHARSET;
02010 
02011     if (hlpfile->version <= 16)
02012     {
02013         char *str = (char*)buf + 0x15;
02014 
02015         hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
02016         if (!hlpfile->lpszTitle) return FALSE;
02017         lstrcpy(hlpfile->lpszTitle, str);
02018         WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
02019         /* Nothing more to parse */
02020         return TRUE;
02021     }
02022     for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
02023     {
02024         char *str = (char*) ptr + 4;
02025         switch (GET_USHORT(ptr, 0))
02026     {
02027     case 1:
02028             if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
02029             hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
02030             if (!hlpfile->lpszTitle) return FALSE;
02031             lstrcpy(hlpfile->lpszTitle, str);
02032             WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
02033             break;
02034 
02035     case 2:
02036             if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
02037             hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
02038             if (!hlpfile->lpszCopyright) return FALSE;
02039             lstrcpy(hlpfile->lpszCopyright, str);
02040             WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
02041             break;
02042 
02043     case 3:
02044             if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
02045             hlpfile->contents_start = GET_UINT(ptr, 4);
02046             WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
02047             break;
02048 
02049     case 4:
02050             macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
02051             if (!macro) break;
02052             p = (char*)macro + sizeof(HLPFILE_MACRO);
02053             lstrcpy(p, str);
02054             macro->lpszMacro = p;
02055             macro->next = 0;
02056             for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
02057             *m = macro;
02058             break;
02059 
02060         case 5:
02061             if (GET_USHORT(ptr, 4 + 4) != 1)
02062                 WINE_FIXME("More than one icon, picking up first\n");
02063             /* 0x16 is sizeof(CURSORICONDIR), see user32/user_private.h */
02064             hlpfile->hIcon = CreateIconFromResourceEx(ptr + 4 + 0x16,
02065                                                       GET_USHORT(ptr, 2) - 0x16, TRUE,
02066                                                       0x30000, 0, 0, 0);
02067             break;
02068 
02069         case 6:
02070             if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
02071 
02072         if (hlpfile->windows) 
02073             hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows, 
02074                                            sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
02075         else 
02076             hlpfile->windows = HeapAlloc(GetProcessHeap(), 0, 
02077                                            sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
02078         
02079             if (hlpfile->windows)
02080             {
02081                 unsigned flags = GET_USHORT(ptr, 4);
02082                 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
02083 
02084                 if (flags & 0x0001) strcpy(wi->type, &str[2]);
02085                 else wi->type[0] = '\0';
02086                 if (flags & 0x0002) strcpy(wi->name, &str[12]);
02087                 else wi->name[0] = '\0';
02088                 if (flags & 0x0004) strcpy(wi->caption, &str[21]);
02089                 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
02090                 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
02091                 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
02092                 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
02093                 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
02094                 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
02095                 wi->win_style = WS_OVERLAPPEDWINDOW;
02096                 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
02097                 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
02098                 WINE_TRACE("System-Window: flags=%c%c%c%c%c%c%c%c type=%s name=%s caption=%s (%d,%d)x(%d,%d)\n",
02099                            flags & 0x0001 ? 'T' : 't',
02100                            flags & 0x0002 ? 'N' : 'n',
02101                            flags & 0x0004 ? 'C' : 'c',
02102                            flags & 0x0008 ? 'X' : 'x',
02103                            flags & 0x0010 ? 'Y' : 'y',
02104                            flags & 0x0020 ? 'W' : 'w',
02105                            flags & 0x0040 ? 'H' : 'h',
02106                            flags & 0x0080 ? 'S' : 's',
02107                            wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
02108                            wi->size.cx, wi->size.cy);
02109             }
02110             break;
02111         case 8:
02112             WINE_WARN("Citation: '%s'\n", ptr + 4);
02113             break;
02114         case 11:
02115             hlpfile->charset = ptr[4];
02116             WINE_TRACE("Charset: %d\n", hlpfile->charset);
02117             break;
02118     default:
02119             WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
02120     }
02121     }
02122     if (!hlpfile->lpszTitle)
02123         hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
02124     return TRUE;
02125 }
02126 
02127 /***********************************************************************
02128  *
02129  *           HLPFILE_GetContext
02130  */
02131 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
02132 {
02133     BYTE                *cbuf, *cend;
02134     unsigned            clen;
02135 
02136     if (!HLPFILE_FindSubFile(hlpfile, "|CONTEXT",  &cbuf, &cend))
02137     {WINE_WARN("context0\n"); return FALSE;}
02138 
02139     clen = cend - cbuf;
02140     hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
02141     if (!hlpfile->Context) return FALSE;
02142     memcpy(hlpfile->Context, cbuf, clen);
02143 
02144     return TRUE;
02145 }
02146 
02147 /***********************************************************************
02148  *
02149  *           HLPFILE_GetKeywords
02150  */
02151 static BOOL HLPFILE_GetKeywords(HLPFILE *hlpfile)
02152 {
02153     BYTE                *cbuf, *cend;
02154     unsigned            clen;
02155 
02156     if (!HLPFILE_FindSubFile(hlpfile, "|KWBTREE", &cbuf, &cend)) return FALSE;
02157     clen = cend - cbuf;
02158     hlpfile->kwbtree = HeapAlloc(GetProcessHeap(), 0, clen);
02159     if (!hlpfile->kwbtree) return FALSE;
02160     memcpy(hlpfile->kwbtree, cbuf, clen);
02161 
02162     if (!HLPFILE_FindSubFile(hlpfile, "|KWDATA", &cbuf, &cend))
02163     {
02164         WINE_ERR("corrupted help file: kwbtree present but kwdata absent\n");
02165         HeapFree(GetProcessHeap(), 0, hlpfile->kwbtree);
02166         return FALSE;
02167     }
02168     clen = cend - cbuf;
02169     hlpfile->kwdata = HeapAlloc(GetProcessHeap(), 0, clen);
02170     if (!hlpfile->kwdata)
02171     {
02172         HeapFree(GetProcessHeap(), 0, hlpfile->kwdata);
02173         return FALSE;
02174     }
02175     memcpy(hlpfile->kwdata, cbuf, clen);
02176 
02177     return TRUE;
02178 }
02179 
02180 /***********************************************************************
02181  *
02182  *           HLPFILE_GetMap
02183  */
02184 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
02185 {
02186     BYTE                *cbuf, *cend;
02187     unsigned            entries, i;
02188 
02189     if (!HLPFILE_FindSubFile(hlpfile, "|CTXOMAP",  &cbuf, &cend))
02190     {WINE_WARN("no map section\n"); return FALSE;}
02191 
02192     entries = GET_USHORT(cbuf, 9);
02193     hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
02194     if (!hlpfile->Map) return FALSE;
02195     hlpfile->wMapLen = entries;
02196     for (i = 0; i < entries; i++)
02197     {
02198         hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
02199         hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
02200     }
02201     return TRUE;
02202 }
02203 
02204 /***********************************************************************
02205  *
02206  *           HLPFILE_GetTOMap
02207  */
02208 static BOOL HLPFILE_GetTOMap(HLPFILE *hlpfile)
02209 {
02210     BYTE                *cbuf, *cend;
02211     unsigned            clen;
02212 
02213     if (!HLPFILE_FindSubFile(hlpfile, "|TOMAP",  &cbuf, &cend))
02214     {WINE_WARN("no tomap section\n"); return FALSE;}
02215 
02216     clen = cend - cbuf - 9;
02217     hlpfile->TOMap = HeapAlloc(GetProcessHeap(), 0, clen);
02218     if (!hlpfile->TOMap) return FALSE;
02219     memcpy(hlpfile->TOMap, cbuf+9, clen);
02220     hlpfile->wTOMapLen = clen/4;
02221     return TRUE;
02222 }
02223 
02224 /***********************************************************************
02225  *
02226  *           DeleteMacro
02227  */
02228 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
02229 {
02230     HLPFILE_MACRO*      next;
02231 
02232     while (macro)
02233     {
02234         next = macro->next;
02235         HeapFree(GetProcessHeap(), 0, macro);
02236         macro = next;
02237     }
02238 }
02239 
02240 /***********************************************************************
02241  *
02242  *           DeletePage
02243  */
02244 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
02245 {
02246     HLPFILE_PAGE* next;
02247 
02248     while (page)
02249     {
02250         next = page->next;
02251         HLPFILE_DeleteMacro(page->first_macro);
02252         HeapFree(GetProcessHeap(), 0, page);
02253         page = next;
02254     }
02255 }
02256 
02257 /***********************************************************************
02258  *
02259  *           HLPFILE_FreeHlpFile
02260  */
02261 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
02262 {
02263     unsigned i;
02264 
02265     if (!hlpfile || --hlpfile->wRefCount > 0) return;
02266 
02267     if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
02268     if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
02269     else first_hlpfile = hlpfile->next;
02270 
02271     if (hlpfile->numFonts)
02272     {
02273         for (i = 0; i < hlpfile->numFonts; i++)
02274         {
02275             DeleteObject(hlpfile->fonts[i].hFont);
02276         }
02277         HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
02278     }
02279 
02280     if (hlpfile->numBmps)
02281     {
02282         for (i = 0; i < hlpfile->numBmps; i++)
02283         {
02284             DeleteObject(hlpfile->bmps[i]);
02285         }
02286         HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
02287     }
02288 
02289     HLPFILE_DeletePage(hlpfile->first_page);
02290     HLPFILE_DeleteMacro(hlpfile->first_macro);
02291 
02292     DestroyIcon(hlpfile->hIcon);
02293     if (hlpfile->numWindows)    HeapFree(GetProcessHeap(), 0, hlpfile->windows);
02294     HeapFree(GetProcessHeap(), 0, hlpfile->Context);
02295     HeapFree(GetProcessHeap(), 0, hlpfile->Map);
02296     HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
02297     HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
02298     HeapFree(GetProcessHeap(), 0, hlpfile->file_buffer);
02299     HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets);
02300     HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer);
02301     HeapFree(GetProcessHeap(), 0, hlpfile->topic_map);
02302     HeapFree(GetProcessHeap(), 0, hlpfile->help_on_file);
02303     HeapFree(GetProcessHeap(), 0, hlpfile);
02304 }
02305 
02306 /***********************************************************************
02307  *
02308  *           HLPFILE_UncompressLZ77_Phrases
02309  */
02310 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
02311 {
02312     UINT i, num, dec_size, head_size;
02313     BYTE *buf, *end;
02314 
02315     if (!HLPFILE_FindSubFile(hlpfile, "|Phrases", &buf, &end)) return FALSE;
02316 
02317     if (hlpfile->version <= 16)
02318         head_size = 13;
02319     else
02320         head_size = 17;
02321 
02322     num = hlpfile->num_phrases = GET_USHORT(buf, 9);
02323     if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
02324 
02325     if (hlpfile->version <= 16)
02326         dec_size = end - buf - 15 - 2 * num;
02327     else
02328         dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
02329 
02330     hlpfile->phrases_offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
02331     hlpfile->phrases_buffer  = HeapAlloc(GetProcessHeap(), 0, dec_size);
02332     if (!hlpfile->phrases_offsets || !hlpfile->phrases_buffer)
02333     {
02334         HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets);
02335         HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer);
02336         return FALSE;
02337     }
02338 
02339     for (i = 0; i <= num; i++)
02340         hlpfile->phrases_offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
02341 
02342     if (hlpfile->version <= 16)
02343         memcpy(hlpfile->phrases_buffer, buf + 15 + 2*num, dec_size);
02344     else
02345         HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)hlpfile->phrases_buffer);
02346 
02347     hlpfile->hasPhrases = TRUE;
02348     return TRUE;
02349 }
02350 
02351 /***********************************************************************
02352  *
02353  *           HLPFILE_Uncompress_Phrases40
02354  */
02355 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
02356 {
02357     UINT num;
02358     INT dec_size, cpr_size;
02359     BYTE *buf_idx, *end_idx;
02360     BYTE *buf_phs, *end_phs;
02361     LONG* ptr, mask = 0;
02362     unsigned int i;
02363     unsigned short bc, n;
02364 
02365     if (!HLPFILE_FindSubFile(hlpfile, "|PhrIndex", &buf_idx, &end_idx) ||
02366         !HLPFILE_FindSubFile(hlpfile, "|PhrImage", &buf_phs, &end_phs)) return FALSE;
02367 
02368     ptr = (LONG*)(buf_idx + 9 + 28);
02369     bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
02370     num = hlpfile->num_phrases = GET_USHORT(buf_idx, 9 + 4);
02371 
02372     WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
02373                "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
02374                GET_UINT(buf_idx, 9 + 0),
02375                GET_UINT(buf_idx, 9 + 4),
02376                GET_UINT(buf_idx, 9 + 8),
02377                GET_UINT(buf_idx, 9 + 12),
02378                GET_UINT(buf_idx, 9 + 16),
02379                GET_UINT(buf_idx, 9 + 20),
02380                GET_USHORT(buf_idx, 9 + 24),
02381                GET_USHORT(buf_idx, 9 + 26));
02382 
02383     dec_size = GET_UINT(buf_idx, 9 + 12);
02384     cpr_size = GET_UINT(buf_idx, 9 + 16);
02385 
02386     if (dec_size != cpr_size &&
02387         dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
02388     {
02389         WINE_WARN("size mismatch %u %u\n",
02390                   dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
02391         dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
02392     }
02393 
02394     hlpfile->phrases_offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
02395     hlpfile->phrases_buffer  = HeapAlloc(GetProcessHeap(), 0, dec_size);
02396     if (!hlpfile->phrases_offsets || !hlpfile->phrases_buffer)
02397     {
02398         HeapFree(GetProcessHeap(), 0, hlpfile->phrases_offsets);
02399         HeapFree(GetProcessHeap(), 0, hlpfile->phrases_buffer);
02400         return FALSE;
02401     }
02402 
02403 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
02404 
02405     hlpfile->phrases_offsets[0] = 0;
02406     for (i = 0; i < num; i++)
02407     {
02408         for (n = 1; getbit(); n += 1 << bc);
02409         if (getbit()) n++;
02410         if (bc > 1 && getbit()) n += 2;
02411         if (bc > 2 && getbit()) n += 4;
02412         if (bc > 3 && getbit()) n += 8;
02413         if (bc > 4 && getbit()) n += 16;
02414         hlpfile->phrases_offsets[i + 1] = hlpfile->phrases_offsets[i] + n;
02415     }
02416 #undef getbit
02417 
02418     if (dec_size == cpr_size)
02419         memcpy(hlpfile->phrases_buffer, buf_phs + 9, dec_size);
02420     else
02421         HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)hlpfile->phrases_buffer);
02422 
02423     hlpfile->hasPhrases40 = TRUE;
02424     return TRUE;
02425 }
02426 
02427 /***********************************************************************
02428  *
02429  *           HLPFILE_Uncompress_Topic
02430  */
02431 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
02432 {
02433     BYTE *buf, *ptr, *end, *newptr;
02434     unsigned int i, newsize = 0;
02435     unsigned int topic_size;
02436 
02437     if (!HLPFILE_FindSubFile(hlpfile, "|TOPIC", &buf, &end))
02438     {WINE_WARN("topic0\n"); return FALSE;}
02439 
02440     buf += 9; /* Skip file header */
02441     topic_size = end - buf;
02442     if (hlpfile->compressed)
02443     {
02444         hlpfile->topic_maplen = (topic_size - 1) / hlpfile->tbsize + 1;
02445 
02446         for (i = 0; i < hlpfile->topic_maplen; i++)
02447         {
02448             ptr = buf + i * hlpfile->tbsize;
02449 
02450             /* I don't know why, it's necessary for printman.hlp */
02451             if (ptr + 0x44 > end) ptr = end - 0x44;
02452 
02453             newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
02454         }
02455 
02456         hlpfile->topic_map = HeapAlloc(GetProcessHeap(), 0,
02457                                        hlpfile->topic_maplen * sizeof(hlpfile->topic_map[0]) + newsize);
02458         if (!hlpfile->topic_map) return FALSE;
02459         newptr = (BYTE*)(hlpfile->topic_map + hlpfile->topic_maplen);
02460         hlpfile->topic_end = newptr + newsize;
02461 
02462         for (i = 0; i < hlpfile->topic_maplen; i++)
02463         {
02464             ptr = buf + i * hlpfile->tbsize;
02465             if (ptr + 0x44 > end) ptr = end - 0x44;
02466 
02467             hlpfile->topic_map[i] = newptr;
02468             newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
02469         }
02470     }
02471     else
02472     {
02473         /* basically, we need to copy the TopicBlockSize byte pages
02474          * (removing the first 0x0C) in one single area in memory
02475          */
02476         hlpfile->topic_maplen = (topic_size - 1) / hlpfile->tbsize + 1;
02477         hlpfile->topic_map = HeapAlloc(GetProcessHeap(), 0,
02478                                        hlpfile->topic_maplen * (sizeof(hlpfile->topic_map[0]) + hlpfile->dsize));
02479         if (!hlpfile->topic_map) return FALSE;
02480         newptr = (BYTE*)(hlpfile->topic_map + hlpfile->topic_maplen);
02481         hlpfile->topic_end = newptr + topic_size;
02482 
02483         for (i = 0; i < hlpfile->topic_maplen; i++)
02484         {
02485             hlpfile->topic_map[i] = newptr + i * hlpfile->dsize;
02486             memcpy(hlpfile->topic_map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
02487         }
02488     }
02489     return TRUE;
02490 }
02491 
02492 /***********************************************************************
02493  *
02494  *           HLPFILE_AddPage
02495  */
02496 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, const BYTE *buf, const BYTE *end, unsigned ref, unsigned offset)
02497 {
02498     HLPFILE_PAGE* page;
02499     const BYTE*   title;
02500     UINT          titlesize, blocksize, datalen;
02501     char*         ptr;
02502     HLPFILE_MACRO*macro;
02503 
02504     blocksize = GET_UINT(buf, 0);
02505     datalen = GET_UINT(buf, 0x10);
02506     title = buf + datalen;
02507     if (title > end) {WINE_WARN("page2\n"); return FALSE;};
02508 
02509     titlesize = GET_UINT(buf, 4);
02510     page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
02511     if (!page) return FALSE;
02512     page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
02513 
02514     if (titlesize > blocksize - datalen)
02515     {
02516         /* need to decompress */
02517         if (hlpfile->hasPhrases)
02518             HLPFILE_Uncompress2(hlpfile, title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
02519         else if (hlpfile->hasPhrases40)
02520             HLPFILE_Uncompress3(hlpfile, page->lpszTitle, page->lpszTitle + titlesize, title, end);
02521         else
02522         {
02523             WINE_FIXME("Text size is too long, splitting\n");
02524             titlesize = blocksize - datalen;
02525             memcpy(page->lpszTitle, title, titlesize);
02526         }
02527     }
02528     else
02529         memcpy(page->lpszTitle, title, titlesize);
02530 
02531     page->lpszTitle[titlesize] = '\0';
02532 
02533     if (hlpfile->first_page)
02534     {
02535         hlpfile->last_page->next = page;
02536         page->prev = hlpfile->last_page;
02537         hlpfile->last_page = page;
02538     }
02539     else
02540     {
02541         hlpfile->first_page = page;
02542         hlpfile->last_page = page;
02543         page->prev = NULL;
02544     }
02545 
02546     page->file            = hlpfile;
02547     page->next            = NULL;
02548     page->first_macro     = NULL;
02549     page->first_link      = NULL;
02550     page->wNumber         = GET_UINT(buf, 0x21);
02551     page->offset          = offset;
02552     page->reference       = ref;
02553 
02554     page->browse_bwd = GET_UINT(buf, 0x19);
02555     page->browse_fwd = GET_UINT(buf, 0x1D);
02556 
02557     if (hlpfile->version <= 16)
02558     {
02559         if (page->browse_bwd == 0xFFFF || page->browse_bwd == 0xFFFFFFFF)
02560             page->browse_bwd = 0xFFFFFFFF;
02561         else
02562             page->browse_bwd = hlpfile->TOMap[page->browse_bwd];
02563 
02564         if (page->browse_fwd == 0xFFFF || page->browse_fwd == 0xFFFFFFFF)
02565             page->browse_fwd = 0xFFFFFFFF;
02566         else
02567             page->browse_fwd = hlpfile->TOMap[page->browse_fwd];
02568     }
02569 
02570     WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
02571                page->wNumber, page->lpszTitle,
02572                page->browse_bwd, page->offset, page->browse_fwd);
02573 
02574     /* now load macros */
02575     ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
02576     while (ptr < page->lpszTitle + titlesize)
02577     {
02578         unsigned len = strlen(ptr);
02579         char*    macro_str;
02580 
02581         WINE_TRACE("macro: %s\n", ptr);
02582         macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
02583         macro->lpszMacro = macro_str = (char*)(macro + 1);
02584         memcpy(macro_str, ptr, len + 1);
02585         /* FIXME: shall we really link macro in reverse order ??
02586          * may produce strange results when played at page opening
02587          */
02588         macro->next = page->first_macro;
02589         page->first_macro = macro;
02590         ptr += len + 1;
02591     }
02592 
02593     return TRUE;
02594 }
02595 
02596 /***********************************************************************
02597  *
02598  *           HLPFILE_SkipParagraph
02599  */
02600 static BOOL HLPFILE_SkipParagraph(HLPFILE *hlpfile, const BYTE *buf, const BYTE *end, unsigned* len)
02601 {
02602     const BYTE  *tmp;
02603 
02604     if (!hlpfile->first_page) {WINE_WARN("no page\n"); return FALSE;};
02605     if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
02606 
02607     tmp = buf + 0x15;
02608     if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
02609     {
02610         fetch_long(&tmp);
02611         *len = fetch_ushort(&tmp);
02612     }
02613     else *len = end-buf-15;
02614 
02615     return TRUE;
02616 }
02617 
02618 /***********************************************************************
02619  *
02620  *           HLPFILE_DoReadHlpFile
02621  */
02622 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
02623 {
02624     BOOL        ret;
02625     HFILE       hFile;
02626     OFSTRUCT    ofs;
02627     BYTE*       buf;
02628     DWORD       ref = 0x0C;
02629     unsigned    index, old_index, offset, len, offs, topicoffset;
02630 
02631     hFile = OpenFile(lpszPath, &ofs, OF_READ);
02632     if (hFile == HFILE_ERROR) return FALSE;
02633 
02634     ret = HLPFILE_ReadFileToBuffer(hlpfile, hFile);
02635     _lclose(hFile);
02636     if (!ret) return FALSE;
02637 
02638     if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
02639 
02640     if (hlpfile->version <= 16 && !HLPFILE_GetTOMap(hlpfile)) return FALSE;
02641 
02642     /* load phrases support */
02643     if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
02644         HLPFILE_Uncompress_Phrases40(hlpfile);
02645 
02646     if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
02647     if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
02648 
02649     buf = hlpfile->topic_map[0];
02650     old_index = -1;
02651     offs = 0;
02652     do
02653     {
02654         BYTE*   end;
02655 
02656         if (hlpfile->version <= 16)
02657         {
02658             index  = (ref - 0x0C) / hlpfile->dsize;
02659             offset = (ref - 0x0C) % hlpfile->dsize;
02660         }
02661         else
02662         {
02663             index  = (ref - 0x0C) >> 14;
02664             offset = (ref - 0x0C) & 0x3FFF;
02665         }
02666 
02667         if (hlpfile->version <= 16 && index != old_index && old_index != -1)
02668         {
02669             /* we jumped to the next block, adjust pointers */
02670             ref -= 12;
02671             offset -= 12;
02672         }
02673 
02674         WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
02675 
02676         if (index >= hlpfile->topic_maplen) {WINE_WARN("maplen\n"); break;}
02677         buf = hlpfile->topic_map[index] + offset;
02678         if (buf + 0x15 >= hlpfile->topic_end) {WINE_WARN("extra\n"); break;}
02679         end = min(buf + GET_UINT(buf, 0), hlpfile->topic_end);
02680         if (index != old_index) {offs = 0; old_index = index;}
02681 
02682         switch (buf[0x14])
02683     {
02684     case 0x02:
02685             if (hlpfile->version <= 16)
02686                 topicoffset = ref + index * 12;
02687             else
02688                 topicoffset = index * 0x8000 + offs;
02689             if (!HLPFILE_AddPage(hlpfile, buf, end, ref, topicoffset)) return FALSE;
02690             break;
02691 
02692     case 0x01:
02693     case 0x20:
02694     case 0x23:
02695             if (!HLPFILE_SkipParagraph(hlpfile, buf, end, &len)) return FALSE;
02696             offs += len;
02697             break;
02698 
02699     default:
02700             WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
02701     }
02702 
02703         if (hlpfile->version <= 16)
02704         {
02705             ref += GET_UINT(buf, 0xc);
02706             if (GET_UINT(buf, 0xc) == 0)
02707                 break;
02708         }
02709         else
02710             ref = GET_UINT(buf, 0xc);
02711     } while (ref != 0xffffffff);
02712 
02713     HLPFILE_GetKeywords(hlpfile);
02714     HLPFILE_GetMap(hlpfile);
02715     if (hlpfile->version <= 16) return TRUE;
02716     return HLPFILE_GetContext(hlpfile);
02717 }
02718 
02719 /***********************************************************************
02720  *
02721  *           HLPFILE_ReadHlpFile
02722  */
02723 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
02724 {
02725     HLPFILE*      hlpfile;
02726 
02727     for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
02728     {
02729         if (!strcmp(lpszPath, hlpfile->lpszPath))
02730         {
02731             hlpfile->wRefCount++;
02732             return hlpfile;
02733         }
02734     }
02735 
02736     hlpfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
02737                         sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
02738     if (!hlpfile) return 0;
02739 
02740     hlpfile->lpszPath           = (char*)hlpfile + sizeof(HLPFILE);
02741     hlpfile->contents_start     = 0xFFFFFFFF;
02742     hlpfile->next               = first_hlpfile;
02743     hlpfile->wRefCount          = 1;
02744 
02745     strcpy(hlpfile->lpszPath, lpszPath);
02746 
02747     first_hlpfile = hlpfile;
02748     if (hlpfile->next) hlpfile->next->prev = hlpfile;
02749 
02750     if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
02751     {
02752         HLPFILE_FreeHlpFile(hlpfile);
02753         hlpfile = 0;
02754     }
02755 
02756     return hlpfile;
02757 }

Generated on Fri May 25 2012 04:15:54 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.