4 * Copyright 1996 Ulrich Schmid
5 * 2002, 2008 Eric Pouech
6 * 2007 Kirill K. Smirnov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 static inline unsigned short GET_USHORT(const BYTE
* buffer
, unsigned i
)
27 return (BYTE
)buffer
[i
] + 0x100 * (BYTE
)buffer
[i
+ 1];
30 static inline short GET_SHORT(const BYTE
* buffer
, unsigned i
)
32 return (BYTE
)buffer
[i
] + 0x100 * (signed char)buffer
[i
+1];
35 static inline unsigned GET_UINT(const BYTE
* buffer
, unsigned i
)
37 return GET_USHORT(buffer
, i
) + 0x10000 * GET_USHORT(buffer
, i
+ 2);
40 static HLPFILE
*first_hlpfile
= 0;
43 /**************************************************************************
44 * HLPFILE_BPTreeSearch
46 * Searches for an element in B+ tree
49 * buf [I] pointer to the embedded file structured as a B+ tree
50 * key [I] pointer to data to find
51 * comp [I] compare function
54 * Pointer to block identified by key, or NULL if failure.
57 static void* HLPFILE_BPTreeSearch(BYTE
* buf
, const void* key
,
58 HLPFILE_BPTreeCompare comp
)
64 BYTE
*pages
, *ptr
, *newptr
;
68 magic
= GET_USHORT(buf
, 9);
71 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic
);
74 page_size
= GET_USHORT(buf
, 9+4);
75 cur_page
= GET_USHORT(buf
, 9+26);
76 level
= GET_USHORT(buf
, 9+32);
80 ptr
= pages
+ cur_page
*page_size
;
81 entries
= GET_SHORT(ptr
, 2);
83 for (i
= 0; i
< entries
; i
++)
85 if (comp(ptr
, key
, 0, (void **)&newptr
) > 0) break;
88 cur_page
= GET_USHORT(ptr
-2, 0);
90 ptr
= pages
+ cur_page
*page_size
;
91 entries
= GET_SHORT(ptr
, 2);
93 for (i
= 0; i
< entries
; i
++)
95 ret
= comp(ptr
, key
, 1, (void **)&newptr
);
96 if (ret
== 0) return ptr
;
97 if (ret
> 0) return NULL
;
103 /**************************************************************************
106 * Enumerates elements in B+ tree.
109 * buf [I] pointer to the embedded file structured as a B+ tree
110 * cb [I] compare function
111 * cookie [IO] cookie for cb function
113 void HLPFILE_BPTreeEnum(BYTE
* buf
, HLPFILE_BPTreeCallback cb
, void* cookie
)
119 BYTE
*pages
, *ptr
, *newptr
;
122 magic
= GET_USHORT(buf
, 9);
125 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic
);
128 page_size
= GET_USHORT(buf
, 9+4);
129 cur_page
= GET_USHORT(buf
, 9+26);
130 level
= GET_USHORT(buf
, 9+32);
131 pages
= buf
+ 9 + 38;
134 ptr
= pages
+ cur_page
*page_size
;
135 cur_page
= GET_USHORT(ptr
, 4);
137 while (cur_page
!= 0xFFFF)
139 ptr
= pages
+ cur_page
*page_size
;
140 entries
= GET_SHORT(ptr
, 2);
142 for (i
= 0; i
< entries
; i
++)
144 cb(ptr
, (void **)&newptr
, cookie
);
147 cur_page
= GET_USHORT(pages
+cur_page
*page_size
, 6);
152 /***********************************************************************
154 * HLPFILE_UncompressedLZ77_Size
156 static INT
HLPFILE_UncompressedLZ77_Size(const BYTE
*ptr
, const BYTE
*end
)
163 for (i
= 0; i
< 8 && ptr
< end
; i
++, mask
>>= 1)
167 int code
= GET_USHORT(ptr
, 0);
168 int len
= 3 + (code
>> 12);
172 else newsize
++, ptr
++;
179 /***********************************************************************
181 * HLPFILE_UncompressLZ77
183 static BYTE
*HLPFILE_UncompressLZ77(const BYTE
*ptr
, const BYTE
*end
, BYTE
*newptr
)
190 for (i
= 0; i
< 8 && ptr
< end
; i
++, mask
>>= 1)
194 int code
= GET_USHORT(ptr
, 0);
195 int len
= 3 + (code
>> 12);
196 int offset
= code
& 0xfff;
198 * We must copy byte-by-byte here. We cannot use memcpy nor
199 * memmove here. Just example:
200 * a[]={1,2,3,4,5,6,7,8,9,10}
204 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
206 for (; len
>0; len
--, newptr
++) *newptr
= *(newptr
-offset
-1);
209 else *newptr
++ = *ptr
++;
216 /***********************************************************************
218 * HLPFILE_Uncompress2
221 static void HLPFILE_Uncompress2(HLPFILE
* hlpfile
, const BYTE
*ptr
, const BYTE
*end
, BYTE
*newptr
, const BYTE
*newend
)
227 while (ptr
< end
&& newptr
< newend
)
229 if (!*ptr
|| *ptr
>= 0x10)
233 code
= 0x100 * ptr
[0] + ptr
[1];
234 index
= (code
- 0x100) / 2;
236 phptr
= (BYTE
*)hlpfile
->phrases_buffer
+ hlpfile
->phrases_offsets
[index
];
237 phend
= (BYTE
*)hlpfile
->phrases_buffer
+ hlpfile
->phrases_offsets
[index
+ 1];
239 if (newptr
+ (phend
- phptr
) > newend
)
241 WINE_FIXME("buffer overflow %p > %p for %lu bytes\n",
242 newptr
, newend
, (SIZE_T
)(phend
- phptr
));
245 memcpy(newptr
, phptr
, phend
- phptr
);
246 newptr
+= phend
- phptr
;
247 if (code
& 1) *newptr
++ = ' ';
252 if (newptr
> newend
) WINE_FIXME("buffer overflow %p > %p\n", newptr
, newend
);
255 /******************************************************************
256 * HLPFILE_Uncompress3
260 static BOOL
HLPFILE_Uncompress3(HLPFILE
* hlpfile
, char* dst
, const char* dst_end
,
261 const BYTE
* src
, const BYTE
* src_end
)
263 unsigned int idx
, len
;
265 for (; src
< src_end
; src
++)
270 if (idx
> hlpfile
->num_phrases
)
272 WINE_ERR("index in phrases %d/%d\n", idx
, hlpfile
->num_phrases
);
277 len
= hlpfile
->phrases_offsets
[idx
+ 1] - hlpfile
->phrases_offsets
[idx
];
278 if (dst
+ len
<= dst_end
)
279 memcpy(dst
, &hlpfile
->phrases_buffer
[hlpfile
->phrases_offsets
[idx
]], len
);
282 else if ((*src
& 0x03) == 0x01)
284 idx
= (*src
+ 1) * 64;
286 if (idx
> hlpfile
->num_phrases
)
288 WINE_ERR("index in phrases %d/%d\n", idx
, hlpfile
->num_phrases
);
293 len
= hlpfile
->phrases_offsets
[idx
+ 1] - hlpfile
->phrases_offsets
[idx
];
294 if (dst
+ len
<= dst_end
)
295 memcpy(dst
, &hlpfile
->phrases_buffer
[hlpfile
->phrases_offsets
[idx
]], len
);
298 else if ((*src
& 0x07) == 0x03)
300 len
= (*src
/ 8) + 1;
301 if (dst
+ len
<= dst_end
)
302 memcpy(dst
, src
+ 1, len
);
307 len
= (*src
/ 16) + 1;
308 if (dst
+ len
<= dst_end
)
309 memset(dst
, ((*src
& 0x0F) == 0x07) ? ' ' : 0, len
);
314 if (dst
> dst_end
) WINE_ERR("buffer overflow (%p > %p)\n", dst
, dst_end
);
318 /******************************************************************
319 * HLPFILE_UncompressRLE
323 static void HLPFILE_UncompressRLE(const BYTE
* src
, const BYTE
* end
, BYTE
* dst
, unsigned dstsz
)
326 BYTE
* sdst
= dst
+ dstsz
;
334 if (dst
+ ch
<= sdst
)
335 memcpy(dst
, src
, ch
);
340 if (dst
+ ch
<= sdst
)
341 memset(dst
, (char)*src
, ch
);
347 WINE_WARN("Buffer X-flow: d(%lu) instead of d(%u)\n",
348 (SIZE_T
)(dst
- (sdst
- dstsz
)), dstsz
);
352 /******************************************************************
353 * HLPFILE_PageByOffset
357 HLPFILE_PAGE
*HLPFILE_PageByOffset(HLPFILE
* hlpfile
, LONG offset
, ULONG
* relative
)
362 if (!hlpfile
) return 0;
364 WINE_TRACE("<%s>[%x]\n", hlpfile
->lpszPath
, offset
);
366 if (offset
== 0xFFFFFFFF) return NULL
;
369 for (found
= NULL
, page
= hlpfile
->first_page
; page
; page
= page
->next
)
371 if (page
->offset
<= offset
&& (!found
|| found
->offset
< page
->offset
))
373 *relative
= offset
- page
->offset
;
378 WINE_ERR("Page of offset %u not found in file %s\n",
379 offset
, hlpfile
->lpszPath
);
383 /***********************************************************************
387 static HLPFILE_PAGE
* HLPFILE_Contents(HLPFILE
*hlpfile
, ULONG
* relative
)
389 HLPFILE_PAGE
* page
= NULL
;
391 if (!hlpfile
) return NULL
;
393 page
= HLPFILE_PageByOffset(hlpfile
, hlpfile
->contents_start
, relative
);
396 page
= hlpfile
->first_page
;
402 /**************************************************************************
405 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
408 static int comp_PageByHash(void *p
, const void *key
,
409 int leaf
, void** next
)
411 LONG lKey
= (LONG_PTR
)key
;
412 LONG lTest
= (INT
)GET_UINT(p
, 0);
414 *next
= (char *)p
+(leaf
?8:6);
415 WINE_TRACE("Comparing '%d' with '%d'\n", lKey
, lTest
);
416 if (lTest
< lKey
) return -1;
417 if (lTest
> lKey
) return 1;
421 /***********************************************************************
425 HLPFILE_PAGE
*HLPFILE_PageByHash(HLPFILE
* hlpfile
, LONG lHash
, ULONG
* relative
)
429 if (!hlpfile
) return NULL
;
430 if (!lHash
) return HLPFILE_Contents(hlpfile
, relative
);
432 WINE_TRACE("<%s>[%x]\n", hlpfile
->lpszPath
, lHash
);
434 /* For win 3.0 files hash values are really page numbers */
435 if (hlpfile
->version
<= 16)
437 if (lHash
>= hlpfile
->wTOMapLen
) return NULL
;
438 return HLPFILE_PageByOffset(hlpfile
, hlpfile
->TOMap
[lHash
], relative
);
441 ptr
= HLPFILE_BPTreeSearch(hlpfile
->Context
, LongToPtr(lHash
), comp_PageByHash
);
444 WINE_ERR("Page of hash %x not found in file %s\n", lHash
, hlpfile
->lpszPath
);
448 return HLPFILE_PageByOffset(hlpfile
, GET_UINT(ptr
, 4), relative
);
451 /***********************************************************************
455 HLPFILE_PAGE
*HLPFILE_PageByMap(HLPFILE
* hlpfile
, LONG lMap
, ULONG
* relative
)
459 if (!hlpfile
) return 0;
461 WINE_TRACE("<%s>[%x]\n", hlpfile
->lpszPath
, lMap
);
463 for (i
= 0; i
< hlpfile
->wMapLen
; i
++)
465 if (hlpfile
->Map
[i
].lMap
== lMap
)
466 return HLPFILE_PageByOffset(hlpfile
, hlpfile
->Map
[i
].offset
, relative
);
469 WINE_ERR("Page of Map %x not found in file %s\n", lMap
, hlpfile
->lpszPath
);
473 /**************************************************************************
476 * HLPFILE_BPTreeCompare function for HLPFILE directory.
479 static int comp_FindSubFile(void *p
, const void *key
,
480 int leaf
, void** next
)
482 *next
= (char *)p
+strlen(p
)+(leaf
?5:3);
483 WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p
, (const char *)key
);
484 return strcmp(p
, key
);
487 /***********************************************************************
489 * HLPFILE_FindSubFile
491 static BOOL
HLPFILE_FindSubFile(HLPFILE
* hlpfile
, LPCSTR name
, BYTE
**subbuf
, BYTE
**subend
)
495 WINE_TRACE("looking for file '%s'\n", name
);
496 ptr
= HLPFILE_BPTreeSearch(hlpfile
->file_buffer
+ GET_UINT(hlpfile
->file_buffer
, 4),
497 name
, comp_FindSubFile
);
498 if (!ptr
) return FALSE
;
499 *subbuf
= hlpfile
->file_buffer
+ GET_UINT(ptr
, strlen(name
)+1);
500 if (*subbuf
>= hlpfile
->file_buffer
+ hlpfile
->file_buffer_size
)
502 WINE_ERR("internal file %s does not fit\n", name
);
505 *subend
= *subbuf
+ GET_UINT(*subbuf
, 0);
506 if (*subend
> hlpfile
->file_buffer
+ hlpfile
->file_buffer_size
)
508 WINE_ERR("internal file %s does not fit\n", name
);
511 if (GET_UINT(*subbuf
, 0) < GET_UINT(*subbuf
, 4) + 9)
513 WINE_ERR("invalid size provided for internal file %s\n", name
);
519 /***********************************************************************
523 LONG
HLPFILE_Hash(LPCSTR lpszContext
)
528 while ((c
= *lpszContext
++))
531 if (c
>= 'A' && c
<= 'Z') x
= c
- 'A' + 17;
532 if (c
>= 'a' && c
<= 'z') x
= c
- 'a' + 17;
533 if (c
>= '1' && c
<= '9') x
= c
- '0';
534 if (c
== '0') x
= 10;
535 if (c
== '.') x
= 12;
536 if (c
== '_') x
= 13;
537 if (x
) lHash
= lHash
* 43 + x
;
542 static LONG
fetch_long(const BYTE
** ptr
)
548 ret
= (*(const ULONG
*)(*ptr
) - 0x80000000) / 2;
553 ret
= (*(const USHORT
*)(*ptr
) - 0x8000) / 2;
560 static ULONG
fetch_ulong(const BYTE
** ptr
)
566 ret
= *(const ULONG
*)(*ptr
) / 2;
571 ret
= *(const USHORT
*)(*ptr
) / 2;
577 static short fetch_short(const BYTE
** ptr
)
583 ret
= (*(const unsigned short*)(*ptr
) - 0x8000) / 2;
588 ret
= (*(const unsigned char*)(*ptr
) - 0x80) / 2;
594 static unsigned short fetch_ushort(const BYTE
** ptr
)
600 ret
= *(const unsigned short*)(*ptr
) / 2;
605 ret
= *(const unsigned char*)(*ptr
) / 2;
611 /******************************************************************
612 * HLPFILE_DecompressGfx
614 * Decompress the data part of a bitmap or a metafile
616 static const BYTE
* HLPFILE_DecompressGfx(const BYTE
* src
, unsigned csz
, unsigned sz
, BYTE packing
,
623 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing
, csz
, sz
);
627 case 0: /* uncompressed */
629 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz
, csz
);
634 dst
= *alloc
= HeapAlloc(GetProcessHeap(), 0, sz
);
635 if (!dst
) return NULL
;
636 HLPFILE_UncompressRLE(src
, src
+ csz
, *alloc
, sz
);
639 sz77
= HLPFILE_UncompressedLZ77_Size(src
, src
+ csz
);
640 dst
= *alloc
= HeapAlloc(GetProcessHeap(), 0, sz77
);
641 if (!dst
) return NULL
;
642 HLPFILE_UncompressLZ77(src
, src
+ csz
, *alloc
);
644 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77
, sz
);
646 case 3: /* LZ77 then RLE */
647 sz77
= HLPFILE_UncompressedLZ77_Size(src
, src
+ csz
);
648 tmp
= HeapAlloc(GetProcessHeap(), 0, sz77
);
649 if (!tmp
) return FALSE
;
650 HLPFILE_UncompressLZ77(src
, src
+ csz
, tmp
);
651 dst
= *alloc
= HeapAlloc(GetProcessHeap(), 0, sz
);
654 HeapFree(GetProcessHeap(), 0, tmp
);
657 HLPFILE_UncompressRLE(tmp
, tmp
+ sz77
, *alloc
, sz
);
658 HeapFree(GetProcessHeap(), 0, tmp
);
661 WINE_FIXME("Unsupported packing %u\n", packing
);
667 static BOOL
HLPFILE_RtfAddRawString(struct RtfData
* rd
, const char* str
, size_t sz
)
669 if (rd
->ptr
+ sz
>= rd
->data
+ rd
->allocated
)
671 char* new = HeapReAlloc(GetProcessHeap(), 0, rd
->data
, rd
->allocated
*= 2);
672 if (!new) return FALSE
;
673 rd
->ptr
= new + (rd
->ptr
- rd
->data
);
676 memcpy(rd
->ptr
, str
, sz
);
682 static BOOL
HLPFILE_RtfAddControl(struct RtfData
* rd
, const char* str
)
684 if (*str
== '\\' || *str
== '{') rd
->in_text
= FALSE
;
685 else if (*str
== '}') rd
->in_text
= TRUE
;
686 return HLPFILE_RtfAddRawString(rd
, str
, strlen(str
));
689 static BOOL
HLPFILE_RtfAddText(struct RtfData
* rd
, const char* str
)
698 if (!HLPFILE_RtfAddRawString(rd
, " ", 1)) return FALSE
;
701 for (last
= p
= str
; *p
; p
++)
703 if (*p
& 0x80) /* escape non-ASCII chars */
706 rlen
= sprintf(xx
, "\\'%x", *(const BYTE
*)p
);
711 case '{': rlen
= 2; replace
= "\\{"; break;
712 case '}': rlen
= 2; replace
= "\\}"; break;
713 case '\\': rlen
= 2; replace
= "\\\\"; break;
716 if ((p
!= last
&& !HLPFILE_RtfAddRawString(rd
, last
, p
- last
)) ||
717 !HLPFILE_RtfAddRawString(rd
, replace
, rlen
)) return FALSE
;
720 return HLPFILE_RtfAddRawString(rd
, last
, p
- last
);
723 /******************************************************************
727 static BOOL
HLPFILE_RtfAddHexBytes(struct RtfData
* rd
, const void* _ptr
, unsigned sz
)
731 const BYTE
* ptr
= _ptr
;
732 static const char* _2hex
= "0123456789abcdef";
736 if (!HLPFILE_RtfAddRawString(rd
, " ", 1)) return FALSE
;
739 for (; sz
; sz
-= step
)
742 for (i
= 0; i
< step
; i
++)
744 tmp
[2 * i
+ 0] = _2hex
[*ptr
>> 4];
745 tmp
[2 * i
+ 1] = _2hex
[*ptr
++ & 0xF];
747 if (!HLPFILE_RtfAddRawString(rd
, tmp
, 2 * step
)) return FALSE
;
752 static HLPFILE_LINK
* HLPFILE_AllocLink(struct RtfData
* rd
, int cookie
,
753 const char* str
, unsigned len
, LONG hash
,
754 unsigned clrChange
, unsigned bHotSpot
, unsigned wnd
);
756 /******************************************************************
757 * HLPFILE_AddHotSpotLinks
760 static void HLPFILE_AddHotSpotLinks(struct RtfData
* rd
, HLPFILE
* file
,
761 const BYTE
* start
, ULONG hs_size
, ULONG hs_offset
)
767 if (hs_size
== 0 || hs_offset
== 0) return;
771 hs_num
= GET_USHORT(start
, 1);
772 hs_macro
= GET_UINT(start
, 3);
774 str
= (const char*)start
+ 7 + 15 * hs_num
+ hs_macro
;
775 /* FIXME: should use hs_size to prevent out of bounds reads */
776 for (i
= 0; i
< hs_num
; i
++)
778 HLPFILE_HOTSPOTLINK
* hslink
;
780 WINE_TRACE("%02x-%02x%02x {%s,%s}\n",
781 start
[7 + 15 * i
+ 0], start
[7 + 15 * i
+ 1], start
[7 + 15 * i
+ 2],
782 str
, str
+ strlen(str
) + 1);
783 /* str points to two null terminated strings:
784 * hotspot name, then link name
786 str
+= strlen(str
) + 1; /* skip hotspot name */
789 switch (start
[7 + 15 * i
+ 0])
790 /* The next two chars always look like 0x04 0x00 ???
791 * What are they for ?
795 hslink
= (HLPFILE_HOTSPOTLINK
*)
796 HLPFILE_AllocLink(rd
, hlp_link_macro
, str
, -1, 0, 0, 1, -1);
801 hslink
= (HLPFILE_HOTSPOTLINK
*)
802 HLPFILE_AllocLink(rd
, (start
[7 + 15 * i
+ 0] & 1) ? hlp_link_link
: hlp_link_popup
,
803 file
->lpszPath
, -1, HLPFILE_Hash(str
),
810 const char* win
= strchr(str
, '>');
816 for (wnd
= file
->numWindows
- 1; wnd
>= 0; wnd
--)
818 if (!strcmp(win
+ 1, file
->windows
[wnd
].name
)) break;
821 WINE_WARN("Couldn't find window info for %s\n", win
);
822 if ((tgt
= HeapAlloc(GetProcessHeap(), 0, win
- str
+ 1)))
824 memcpy(tgt
, str
, win
- str
);
825 tgt
[win
- str
] = '\0';
828 hslink
= (HLPFILE_HOTSPOTLINK
*)
829 HLPFILE_AllocLink(rd
, (start
[7 + 15 * i
+ 0] & 1) ? hlp_link_link
: hlp_link_popup
,
830 file
->lpszPath
, -1, HLPFILE_Hash(tgt
? tgt
: str
), 0, 1, wnd
);
831 HeapFree(GetProcessHeap(), 0, tgt
);
835 WINE_FIXME("unknown hotsport target 0x%x\n", start
[7 + 15 * i
+ 0]);
839 hslink
->x
= GET_USHORT(start
, 7 + 15 * i
+ 3);
840 hslink
->y
= GET_USHORT(start
, 7 + 15 * i
+ 5);
841 hslink
->width
= GET_USHORT(start
, 7 + 15 * i
+ 7);
842 hslink
->height
= GET_USHORT(start
, 7 + 15 * i
+ 9);
843 /* target = GET_UINT(start, 7 + 15 * i + 11); */
845 str
+= strlen(str
) + 1;
849 /******************************************************************
850 * HLPFILE_RtfAddTransparentBitmap
852 * We'll transform a transparent bitmap into an metafile that
853 * we then transform into RTF
855 static BOOL
HLPFILE_RtfAddTransparentBitmap(struct RtfData
* rd
, const BITMAPINFO
* bi
,
856 const void* pict
, unsigned nc
)
858 HDC hdc
, hdcMask
, hdcMem
, hdcEMF
;
859 HBITMAP hbm
, hbmMask
, hbmOldMask
, hbmOldMem
;
865 hbm
= CreateDIBitmap(hdc
= GetDC(0), &bi
->bmiHeader
,
866 CBM_INIT
, pict
, bi
, DIB_RGB_COLORS
);
868 hdcMem
= CreateCompatibleDC(hdc
);
869 hbmOldMem
= SelectObject(hdcMem
, hbm
);
871 /* create the mask bitmap from the main bitmap */
872 hdcMask
= CreateCompatibleDC(hdc
);
873 hbmMask
= CreateBitmap(bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
, 1, 1, NULL
);
874 hbmOldMask
= SelectObject(hdcMask
, hbmMask
);
876 RGB(bi
->bmiColors
[nc
- 1].rgbRed
,
877 bi
->bmiColors
[nc
- 1].rgbGreen
,
878 bi
->bmiColors
[nc
- 1].rgbBlue
));
879 BitBlt(hdcMask
, 0, 0, bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
, hdcMem
, 0, 0, SRCCOPY
);
881 /* sets to RGB(0,0,0) the transparent bits in main bitmap */
882 SetBkColor(hdcMem
, RGB(0,0,0));
883 SetTextColor(hdcMem
, RGB(255,255,255));
884 BitBlt(hdcMem
, 0, 0, bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
, hdcMask
, 0, 0, SRCAND
);
886 SelectObject(hdcMask
, hbmOldMask
);
889 SelectObject(hdcMem
, hbmOldMem
);
892 /* we create the bitmap on the fly */
893 hdcEMF
= CreateEnhMetaFileW(NULL
, NULL
, NULL
, NULL
);
894 hdcMem
= CreateCompatibleDC(hdcEMF
);
896 /* sets to RGB(0,0,0) the transparent bits in final bitmap */
897 hbmOldMem
= SelectObject(hdcMem
, hbmMask
);
898 SetBkColor(hdcEMF
, RGB(255, 255, 255));
899 SetTextColor(hdcEMF
, RGB(0, 0, 0));
900 BitBlt(hdcEMF
, 0, 0, bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
, hdcMem
, 0, 0, SRCAND
);
902 /* and copy the remaining bits of main bitmap */
903 SelectObject(hdcMem
, hbm
);
904 BitBlt(hdcEMF
, 0, 0, bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
, hdcMem
, 0, 0, SRCPAINT
);
905 SelectObject(hdcMem
, hbmOldMem
);
910 DeleteObject(hbmMask
);
913 hEMF
= CloseEnhMetaFile(hdcEMF
);
915 /* generate rtf stream */
916 sz
= GetEnhMetaFileBits(hEMF
, 0, NULL
);
917 if (sz
&& (data
= HeapAlloc(GetProcessHeap(), 0, sz
)))
919 if (sz
== GetEnhMetaFileBits(hEMF
, sz
, data
))
921 ret
= HLPFILE_RtfAddControl(rd
, "{\\pict\\emfblip") &&
922 HLPFILE_RtfAddHexBytes(rd
, data
, sz
) &&
923 HLPFILE_RtfAddControl(rd
, "}");
925 HeapFree(GetProcessHeap(), 0, data
);
927 DeleteEnhMetaFile(hEMF
);
932 /******************************************************************
933 * HLPFILE_RtfAddBitmap
936 static BOOL
HLPFILE_RtfAddBitmap(struct RtfData
* rd
, HLPFILE
* file
, const BYTE
* beg
, BYTE type
, BYTE pack
)
939 const BYTE
* pict_beg
;
944 BOOL clrImportant
= FALSE
;
947 unsigned hs_size
, hs_offset
;
949 bi
= HeapAlloc(GetProcessHeap(), 0, sizeof(*bi
));
950 if (!bi
) return FALSE
;
952 ptr
= beg
+ 2; /* for type and pack */
954 bi
->bmiHeader
.biSize
= sizeof(bi
->bmiHeader
);
955 bi
->bmiHeader
.biXPelsPerMeter
= fetch_ulong(&ptr
);
956 bi
->bmiHeader
.biYPelsPerMeter
= fetch_ulong(&ptr
);
957 bi
->bmiHeader
.biPlanes
= fetch_ushort(&ptr
);
958 bi
->bmiHeader
.biBitCount
= fetch_ushort(&ptr
);
959 bi
->bmiHeader
.biWidth
= fetch_ulong(&ptr
);
960 bi
->bmiHeader
.biHeight
= fetch_ulong(&ptr
);
961 bi
->bmiHeader
.biClrUsed
= fetch_ulong(&ptr
);
962 clrImportant
= fetch_ulong(&ptr
);
963 bi
->bmiHeader
.biClrImportant
= (clrImportant
> 1) ? clrImportant
: 0;
964 bi
->bmiHeader
.biCompression
= BI_RGB
;
965 if (bi
->bmiHeader
.biBitCount
> 32) WINE_FIXME("Unknown bit count %u\n", bi
->bmiHeader
.biBitCount
);
966 if (bi
->bmiHeader
.biPlanes
!= 1) WINE_FIXME("Unsupported planes %u\n", bi
->bmiHeader
.biPlanes
);
967 bi
->bmiHeader
.biSizeImage
= (((bi
->bmiHeader
.biWidth
* bi
->bmiHeader
.biBitCount
+ 31) & ~31) / 8) * bi
->bmiHeader
.biHeight
;
968 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
969 bi
->bmiHeader
.biPlanes
, bi
->bmiHeader
.biBitCount
,
970 bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
);
972 csz
= fetch_ulong(&ptr
);
973 hs_size
= fetch_ulong(&ptr
);
975 off
= GET_UINT(ptr
, 0); ptr
+= 4;
976 hs_offset
= GET_UINT(ptr
, 0); ptr
+= 4;
977 HLPFILE_AddHotSpotLinks(rd
, file
, beg
, hs_size
, hs_offset
);
979 /* now read palette info */
984 nc
= bi
->bmiHeader
.biClrUsed
;
985 /* not quite right, especially for bitfields type of compression */
986 if (!nc
&& bi
->bmiHeader
.biBitCount
<= 8)
987 nc
= 1 << bi
->bmiHeader
.biBitCount
;
989 bi
= HeapReAlloc(GetProcessHeap(), 0, bi
, sizeof(*bi
) + nc
* sizeof(RGBQUAD
));
990 if (!bi
) return FALSE
;
991 for (i
= 0; i
< nc
; i
++)
993 bi
->bmiColors
[i
].rgbBlue
= ptr
[0];
994 bi
->bmiColors
[i
].rgbGreen
= ptr
[1];
995 bi
->bmiColors
[i
].rgbRed
= ptr
[2];
996 bi
->bmiColors
[i
].rgbReserved
= 0;
1000 pict_beg
= HLPFILE_DecompressGfx(beg
+ off
, csz
, bi
->bmiHeader
.biSizeImage
, pack
, &alloc
);
1002 if (clrImportant
== 1 && nc
> 0)
1004 ret
= HLPFILE_RtfAddTransparentBitmap(rd
, bi
, pict_beg
, nc
);
1007 if (!HLPFILE_RtfAddControl(rd
, "{\\pict")) goto done
;
1010 sprintf(tmp
, "\\dibitmap0\\picw%d\\pich%d",
1011 bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
);
1012 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1013 if (!HLPFILE_RtfAddHexBytes(rd
, bi
, sizeof(*bi
) + nc
* sizeof(RGBQUAD
))) goto done
;
1017 sprintf(tmp
, "\\wbitmap0\\wbmbitspixel%d\\wbmplanes%d\\picw%d\\pich%d",
1018 bi
->bmiHeader
.biBitCount
, bi
->bmiHeader
.biPlanes
,
1019 bi
->bmiHeader
.biWidth
, bi
->bmiHeader
.biHeight
);
1020 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1022 if (!HLPFILE_RtfAddHexBytes(rd
, pict_beg
, bi
->bmiHeader
.biSizeImage
)) goto done
;
1023 if (!HLPFILE_RtfAddControl(rd
, "}")) goto done
;
1027 HeapFree(GetProcessHeap(), 0, bi
);
1028 HeapFree(GetProcessHeap(), 0, alloc
);
1033 /******************************************************************
1034 * HLPFILE_RtfAddMetaFile
1037 static BOOL
HLPFILE_RtfAddMetaFile(struct RtfData
* rd
, HLPFILE
* file
, const BYTE
* beg
, BYTE pack
)
1039 ULONG size
, csize
, off
, hs_offset
, hs_size
;
1047 WINE_TRACE("Loading metafile\n");
1049 ptr
= beg
+ 2; /* for type and pack */
1051 mm
= fetch_ushort(&ptr
); /* mapping mode */
1052 sprintf(tmp
, "{\\pict\\wmetafile%u\\picw%u\\pich%u",
1053 mm
, GET_USHORT(ptr
, 0), GET_USHORT(ptr
, 2));
1054 if (!HLPFILE_RtfAddControl(rd
, tmp
)) return FALSE
;
1057 size
= fetch_ulong(&ptr
); /* decompressed size */
1058 csize
= fetch_ulong(&ptr
); /* compressed size */
1059 hs_size
= fetch_ulong(&ptr
); /* hotspot size */
1060 off
= GET_UINT(ptr
, 0);
1061 hs_offset
= GET_UINT(ptr
, 4);
1064 HLPFILE_AddHotSpotLinks(rd
, file
, beg
, hs_size
, hs_offset
);
1066 WINE_TRACE("sz=%u csz=%u offs=%u/%u,%u/%u\n",
1067 size
, csize
, off
, (ULONG
)(ptr
- beg
), hs_size
, hs_offset
);
1069 bits
= HLPFILE_DecompressGfx(beg
+ off
, csize
, size
, pack
, &alloc
);
1070 if (!bits
) return FALSE
;
1072 ret
= HLPFILE_RtfAddHexBytes(rd
, bits
, size
) &&
1073 HLPFILE_RtfAddControl(rd
, "}");
1075 HeapFree(GetProcessHeap(), 0, alloc
);
1080 /******************************************************************
1081 * HLPFILE_RtfAddGfxByAddr
1084 static BOOL
HLPFILE_RtfAddGfxByAddr(struct RtfData
* rd
, HLPFILE
*hlpfile
,
1085 const BYTE
* ref
, ULONG size
)
1087 unsigned i
, numpict
;
1089 numpict
= GET_USHORT(ref
, 2);
1090 WINE_TRACE("Got picture magic=%04x #=%d\n", GET_USHORT(ref
, 0), numpict
);
1092 for (i
= 0; i
< numpict
; i
++)
1098 WINE_TRACE("Offset[%d] = %x\n", i
, GET_UINT(ref
, (1 + i
) * 4));
1099 beg
= ptr
= ref
+ GET_UINT(ref
, (1 + i
) * 4);
1106 case 5: /* device dependent bmp */
1107 case 6: /* device independent bmp */
1108 HLPFILE_RtfAddBitmap(rd
, hlpfile
, beg
, type
, pack
);
1111 HLPFILE_RtfAddMetaFile(rd
, hlpfile
, beg
, pack
);
1113 default: WINE_FIXME("Unknown type %u\n", type
); return FALSE
;
1116 /* FIXME: hotspots */
1118 /* FIXME: implement support for multiple picture format */
1119 if (numpict
!= 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
1125 /******************************************************************
1126 * HLPFILE_RtfAddGfxByIndex
1130 static BOOL
HLPFILE_RtfAddGfxByIndex(struct RtfData
* rd
, HLPFILE
*hlpfile
,
1136 WINE_TRACE("Loading picture #%d\n", index
);
1138 sprintf(tmp
, "|bm%u", index
);
1140 if (!HLPFILE_FindSubFile(hlpfile
, tmp
, &ref
, &end
)) {WINE_WARN("no sub file\n"); return FALSE
;}
1143 return HLPFILE_RtfAddGfxByAddr(rd
, hlpfile
, ref
, end
- ref
);
1146 /******************************************************************
1151 static HLPFILE_LINK
* HLPFILE_AllocLink(struct RtfData
* rd
, int cookie
,
1152 const char* str
, unsigned len
, LONG hash
,
1153 unsigned clrChange
, unsigned bHotSpot
, unsigned wnd
)
1157 unsigned asz
= bHotSpot
? sizeof(HLPFILE_HOTSPOTLINK
) : sizeof(HLPFILE_LINK
);
1159 /* FIXME: should build a string table for the attributes.link.lpszPath
1160 * they are reallocated for each link
1162 if (len
== -1) len
= strlen(str
);
1163 link
= HeapAlloc(GetProcessHeap(), 0, asz
+ len
+ 1);
1164 if (!link
) return NULL
;
1166 link
->cookie
= cookie
;
1167 link
->string
= link_str
= (char*)link
+ asz
;
1168 memcpy(link_str
, str
, len
);
1169 link_str
[len
] = '\0';
1171 link
->bClrChange
= clrChange
? 1 : 0;
1172 link
->bHotSpot
= bHotSpot
;
1174 link
->next
= rd
->first_link
;
1175 rd
->first_link
= link
;
1176 link
->cpMin
= rd
->char_pos
;
1177 rd
->force_color
= clrChange
;
1178 if (rd
->current_link
) WINE_FIXME("Pending link\n");
1180 link
->cpMax
= rd
->char_pos
;
1182 rd
->current_link
= link
;
1184 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
1185 link
->cookie
, link
->string
, link
->hash
, link
->window
);
1189 static unsigned HLPFILE_HalfPointsToTwips(unsigned pts
)
1191 static unsigned logPxY
;
1194 HDC hdc
= GetDC(NULL
);
1195 logPxY
= GetDeviceCaps(hdc
, LOGPIXELSY
);
1196 ReleaseDC(NULL
, hdc
);
1198 return MulDiv(pts
, 72 * 10, logPxY
);
1201 /***********************************************************************
1203 * HLPFILE_BrowseParagraph
1205 static BOOL
HLPFILE_BrowseParagraph(HLPFILE_PAGE
* page
, struct RtfData
* rd
,
1206 BYTE
*buf
, BYTE
* end
, unsigned* parlen
)
1209 const BYTE
*format
, *format_end
;
1210 char *text
, *text_base
, *text_end
;
1211 LONG size
, blocksize
, datalen
;
1212 unsigned short bits
;
1213 unsigned nc
, ncol
= 1;
1215 BOOL in_table
= FALSE
;
1219 if (buf
+ 0x19 > end
) {WINE_WARN("header too small\n"); return FALSE
;};
1222 blocksize
= GET_UINT(buf
, 0);
1223 size
= GET_UINT(buf
, 0x4);
1224 datalen
= GET_UINT(buf
, 0x10);
1225 text
= text_base
= HeapAlloc(GetProcessHeap(), 0, size
);
1226 if (!text
) return FALSE
;
1227 if (size
> blocksize
- datalen
)
1229 /* need to decompress */
1230 if (page
->file
->hasPhrases
)
1231 HLPFILE_Uncompress2(page
->file
, buf
+ datalen
, end
, (BYTE
*)text
, (BYTE
*)text
+ size
);
1232 else if (page
->file
->hasPhrases40
)
1233 HLPFILE_Uncompress3(page
->file
, text
, text
+ size
, buf
+ datalen
, end
);
1236 WINE_FIXME("Text size is too long, splitting\n");
1237 size
= blocksize
- datalen
;
1238 memcpy(text
, buf
+ datalen
, size
);
1242 memcpy(text
, buf
+ datalen
, size
);
1244 text_end
= text
+ size
;
1246 format
= buf
+ 0x15;
1247 format_end
= buf
+ GET_UINT(buf
, 0x10);
1249 if (buf
[0x14] == 0x20 || buf
[0x14] == 0x23)
1251 fetch_long(&format
);
1252 *parlen
= fetch_ushort(&format
);
1255 if (buf
[0x14] == 0x23)
1262 if (!HLPFILE_RtfAddControl(rd
, "\\trowd")) goto done
;
1264 if (type
== 0 || type
== 2)
1266 table_width
= GET_SHORT(format
, 0);
1270 table_width
= 32767;
1271 WINE_TRACE("New table: cols=%d type=%x width=%d\n",
1272 ncol
, type
, table_width
);
1276 sprintf(tmp
, "\\trgaph%d\\trleft%d",
1277 HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format
, 6), table_width
, 32767)),
1278 HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format
, 0), table_width
, 32767)));
1279 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1280 pos
= HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format
, 6) / 2, table_width
, 32767));
1281 for (nc
= 0; nc
< ncol
; nc
++)
1283 WINE_TRACE("column(%d/%d) gap=%d width=%d\n",
1284 nc
, ncol
, GET_SHORT(format
, nc
*4),
1285 GET_SHORT(format
, nc
*4+2));
1286 pos
+= GET_SHORT(format
, nc
* 4) + GET_SHORT(format
, nc
* 4 + 2);
1287 sprintf(tmp
, "\\cellx%d",
1288 HLPFILE_HalfPointsToTwips(MulDiv(pos
, table_width
, 32767)));
1289 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1294 WINE_TRACE("column(0/%d) gap=%d width=%d\n",
1295 ncol
, GET_SHORT(format
, 0), GET_SHORT(format
, 2));
1296 sprintf(tmp
, "\\trleft%d\\cellx%d ",
1297 HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format
, 0), table_width
, 32767)),
1298 HLPFILE_HalfPointsToTwips(MulDiv(GET_SHORT(format
, 0) + GET_SHORT(format
, 2),
1299 table_width
, 32767)));
1300 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1305 for (nc
= 0; nc
< ncol
; /**/)
1307 WINE_TRACE("looking for format at offset %lu in column %d\n", (SIZE_T
)(format
- (buf
+ 0x15)), nc
);
1308 if (!HLPFILE_RtfAddControl(rd
, "\\pard")) goto done
;
1311 nc
= GET_SHORT(format
, 0);
1312 if (nc
== -1) break;
1314 if (!HLPFILE_RtfAddControl(rd
, "\\intbl")) goto done
;
1317 if (buf
[0x14] == 0x01)
1321 bits
= GET_USHORT(format
, 0); format
+= 2;
1322 if (bits
& 0x0001) fetch_long(&format
);
1325 sprintf(tmp
, "\\sb%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1326 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1330 sprintf(tmp
, "\\sa%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1331 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1335 sprintf(tmp
, "\\sl%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1336 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1340 sprintf(tmp
, "\\li%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1341 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1345 sprintf(tmp
, "\\ri%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1346 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1350 sprintf(tmp
, "\\fi%d", HLPFILE_HalfPointsToTwips(fetch_short(&format
)));
1351 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1355 BYTE brdr
= *format
++;
1358 if ((brdr
& 0x01) && !HLPFILE_RtfAddControl(rd
, "\\box")) goto done
;
1359 if ((brdr
& 0x02) && !HLPFILE_RtfAddControl(rd
, "\\brdrt")) goto done
;
1360 if ((brdr
& 0x04) && !HLPFILE_RtfAddControl(rd
, "\\brdrl")) goto done
;
1361 if ((brdr
& 0x08) && !HLPFILE_RtfAddControl(rd
, "\\brdrb")) goto done
;
1362 if ((brdr
& 0x10) && !HLPFILE_RtfAddControl(rd
, "\\brdrr")) goto done
;
1363 if ((brdr
& 0x20) && !HLPFILE_RtfAddControl(rd
, "\\brdrth")) goto done
;
1364 if (!(brdr
& 0x20) && !HLPFILE_RtfAddControl(rd
, "\\brdrs")) goto done
;
1365 if ((brdr
& 0x40) && !HLPFILE_RtfAddControl(rd
, "\\brdrdb")) goto done
;
1368 w
= GET_SHORT(format
, 0); format
+= 2;
1371 sprintf(tmp
, "\\brdrw%d", HLPFILE_HalfPointsToTwips(w
));
1372 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1377 int i
, ntab
= fetch_short(&format
);
1381 for (i
= 0; i
< ntab
; i
++)
1383 tab
= fetch_ushort(&format
);
1384 ts
= (tab
& 0x4000) ? fetch_ushort(&format
) : 0 /* left */;
1387 default: WINE_FIXME("Unknown tab style %x\n", ts
);
1389 case 0: kind
= ""; break;
1390 case 1: kind
= "\\tqr"; break;
1391 case 2: kind
= "\\tqc"; break;
1393 /* FIXME: do kind */
1394 sprintf(tmp
, "%s\\tx%d",
1395 kind
, HLPFILE_HalfPointsToTwips(tab
& 0x3FFF));
1396 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1399 switch (bits
& 0xc00)
1401 default: WINE_FIXME("Unsupported alignment 0xC00\n"); break;
1402 case 0: if (!HLPFILE_RtfAddControl(rd
, "\\ql")) goto done
; break;
1403 case 0x400: if (!HLPFILE_RtfAddControl(rd
, "\\qr")) goto done
; break;
1404 case 0x800: if (!HLPFILE_RtfAddControl(rd
, "\\qc")) goto done
; break;
1407 /* 0x1000 doesn't need space */
1408 if ((bits
& 0x1000) && !HLPFILE_RtfAddControl(rd
, "\\keep")) goto done
;
1409 if ((bits
& 0xE080) != 0)
1410 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits
);
1412 while (text
< text_end
&& format
< format_end
)
1414 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text
), text
, text_end
, format
, format_end
);
1415 textsize
= strlen(text
);
1418 if (rd
->force_color
)
1420 if ((rd
->current_link
->cookie
== hlp_link_popup
) ?
1421 !HLPFILE_RtfAddControl(rd
, "{\\uld\\cf1") :
1422 !HLPFILE_RtfAddControl(rd
, "{\\ul\\cf1")) goto done
;
1424 if (!HLPFILE_RtfAddText(rd
, text
)) goto done
;
1425 if (rd
->force_color
&& !HLPFILE_RtfAddControl(rd
, "}")) goto done
;
1426 rd
->char_pos
+= textsize
;
1428 /* else: null text, keep on storing attributes */
1429 text
+= textsize
+ 1;
1431 if (*format
== 0xff)
1437 WINE_TRACE("format=%02x\n", *format
);
1441 WINE_FIXME("NIY20\n");
1446 WINE_FIXME("NIY21\n");
1452 unsigned font
= GET_USHORT(format
, 1);
1455 WINE_TRACE("Changing font to %d\n", font
);
1457 /* Font size in hlpfile is given in the same units as
1458 rtf control word \fs uses (half-points). */
1459 switch (rd
->font_scale
)
1461 case 0: fs
= page
->file
->fonts
[font
].LogFont
.lfHeight
- 4; break;
1463 case 1: fs
= page
->file
->fonts
[font
].LogFont
.lfHeight
; break;
1464 case 2: fs
= page
->file
->fonts
[font
].LogFont
.lfHeight
+ 4; break;
1466 /* FIXME: missing at least colors, also bold attribute looses information */
1468 sprintf(tmp
, "\\f%u\\cf%u\\fs%u%s%s%s%s",
1470 page
->file
->fonts
[font
].LogFont
.lfWeight
> 400 ? "\\b" : "\\b0",
1471 page
->file
->fonts
[font
].LogFont
.lfItalic
? "\\i" : "\\i0", page
->file
->fonts
[font
].LogFont
.lfUnderline
? "\\ul" : "\\ul0",
1472 page
->file
->fonts
[font
].LogFont
.lfStrikeOut
? "\\strike" : "\\strike0");
1473 if (!HLPFILE_RtfAddControl(rd
, tmp
)) goto done
;
1478 if (!HLPFILE_RtfAddControl(rd
, "\\line")) goto done
;
1486 if (format
[1] != 0xFF)
1488 if (!HLPFILE_RtfAddControl(rd
, "\\par\\intbl")) goto done
;
1492 if (!HLPFILE_RtfAddControl(rd
, "\\cell\\pard\\intbl")) goto done
;
1495 else if (!HLPFILE_RtfAddControl(rd
, "\\par")) goto done
;
1501 if (!HLPFILE_RtfAddControl(rd
, "\\tab")) goto done
;
1516 BYTE type
= format
[1];
1518 /* FIXME: we don't use 'BYTE pos = (*format - 0x86);' for the image position */
1520 size
= fetch_long(&format
);
1525 fetch_ushort(&format
); /* hot spot */
1528 switch (GET_SHORT(format
, 0))
1531 HLPFILE_RtfAddGfxByIndex(rd
, page
->file
, GET_SHORT(format
, 2));
1535 WINE_FIXME("does it work ??? %x<%u>#%u\n",
1536 GET_SHORT(format
, 0),
1537 size
, GET_SHORT(format
, 2));
1538 HLPFILE_RtfAddGfxByAddr(rd
, page
->file
, format
+ 2, size
- 4);
1542 WINE_FIXME("??? %u\n", GET_SHORT(format
, 0));
1547 WINE_FIXME("Got an embedded element %s\n", format
+ 6);
1550 WINE_FIXME("Got a type %d picture\n", type
);
1559 if (!rd
->current_link
)
1560 WINE_FIXME("No existing link\n");
1561 rd
->current_link
->cpMax
= rd
->char_pos
;
1562 rd
->current_link
= NULL
;
1563 rd
->force_color
= FALSE
;
1567 if (!HLPFILE_RtfAddControl(rd
, "\\~")) goto done
;
1573 if (!HLPFILE_RtfAddControl(rd
, "\\_")) goto done
;
1574 /* FIXME: it could be that hyphen is also in input stream !! */
1587 WINE_TRACE("macro => %s\n", format
+ 3);
1588 HLPFILE_AllocLink(rd
, hlp_link_macro
, (const char*)format
+ 3,
1589 GET_USHORT(format
, 1), 0, !(*format
& 4), 0, -1);
1590 format
+= 3 + GET_USHORT(format
, 1);
1595 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format
, 1));
1596 HLPFILE_AllocLink(rd
, (*format
& 1) ? hlp_link_link
: hlp_link_popup
,
1597 page
->file
->lpszPath
, -1, GET_UINT(format
, 1), 1, 0, -1);
1607 HLPFILE_AllocLink(rd
, (*format
& 1) ? hlp_link_link
: hlp_link_popup
,
1608 page
->file
->lpszPath
, -1, GET_UINT(format
, 1),
1609 !(*format
& 4), 0, -1);
1618 const char* ptr
= (const char*) format
+ 8;
1619 BYTE type
= format
[3];
1628 ptr
= page
->file
->lpszPath
;
1631 for (wnd
= page
->file
->numWindows
- 1; wnd
>= 0; wnd
--)
1633 if (!strcmp(ptr
, page
->file
->windows
[wnd
].name
)) break;
1636 WINE_WARN("Couldn't find window info for %s\n", ptr
);
1637 ptr
+= strlen(ptr
) + 1;
1642 WINE_WARN("Unknown link type %d\n", type
);
1645 HLPFILE_AllocLink(rd
, (*format
& 1) ? hlp_link_link
: hlp_link_popup
,
1646 ptr
, -1, GET_UINT(format
, 4), !(*format
& 4), 0, wnd
);
1648 format
+= 3 + GET_USHORT(format
, 1);
1652 WINE_WARN("format %02x\n", *format
);
1659 if (!HLPFILE_RtfAddControl(rd
, "\\row\\par\\pard\\plain")) goto done
;
1665 HeapFree(GetProcessHeap(), 0, text_base
);
1669 /******************************************************************
1670 * HLPFILE_BrowsePage
1673 BOOL
HLPFILE_BrowsePage(HLPFILE_PAGE
* page
, struct RtfData
* rd
,
1674 unsigned font_scale
, unsigned relative
)
1676 HLPFILE
*hlpfile
= page
->file
;
1678 DWORD ref
= page
->reference
;
1679 unsigned index
, old_index
= -1, offset
, count
= 0, offs
= 0;
1680 unsigned cpg
, parlen
;
1682 const char* ck
= NULL
;
1685 rd
->data
= rd
->ptr
= HeapAlloc(GetProcessHeap(), 0, rd
->allocated
= 32768);
1687 rd
->first_link
= rd
->current_link
= NULL
;
1688 rd
->force_color
= FALSE
;
1689 rd
->font_scale
= font_scale
;
1690 rd
->relative
= relative
;
1691 rd
->char_pos_rel
= 0;
1693 switch (hlpfile
->charset
)
1695 case DEFAULT_CHARSET
:
1696 case ANSI_CHARSET
: cpg
= 1252; break;
1697 case SHIFTJIS_CHARSET
: cpg
= 932; break;
1698 case HANGEUL_CHARSET
: cpg
= 949; break;
1699 case GB2312_CHARSET
: cpg
= 936; break;
1700 case CHINESEBIG5_CHARSET
: cpg
= 950; break;
1701 case GREEK_CHARSET
: cpg
= 1253; break;
1702 case TURKISH_CHARSET
: cpg
= 1254; break;
1703 case HEBREW_CHARSET
: cpg
= 1255; break;
1704 case ARABIC_CHARSET
: cpg
= 1256; break;
1705 case BALTIC_CHARSET
: cpg
= 1257; break;
1706 case VIETNAMESE_CHARSET
: cpg
= 1258; break;
1707 case RUSSIAN_CHARSET
: cpg
= 1251; break;
1708 case EE_CHARSET
: cpg
= 1250; break;
1709 case THAI_CHARSET
: cpg
= 874; break;
1710 case JOHAB_CHARSET
: cpg
= 1361; break;
1711 case MAC_CHARSET
: ck
= "mac"; break;
1713 WINE_FIXME("Unsupported charset %u\n", hlpfile
->charset
);
1718 sprintf(tmp
, "{\\rtf1\\%s\\deff0", ck
);
1719 if (!HLPFILE_RtfAddControl(rd
, tmp
)) return FALSE
;
1723 sprintf(tmp
, "{\\rtf1\\ansi\\ansicpg%d\\deff0", cpg
);
1724 if (!HLPFILE_RtfAddControl(rd
, tmp
)) return FALSE
;
1727 /* generate font table */
1728 if (!HLPFILE_RtfAddControl(rd
, "{\\fonttbl")) return FALSE
;
1729 for (index
= 0; index
< hlpfile
->numFonts
; index
++)
1732 switch (hlpfile
->fonts
[index
].LogFont
.lfPitchAndFamily
& 0xF0)
1734 case FF_MODERN
: family
= "modern"; break;
1735 case FF_ROMAN
: family
= "roman"; break;
1736 case FF_SWISS
: family
= "swiss"; break;
1737 case FF_SCRIPT
: family
= "script"; break;
1738 case FF_DECORATIVE
: family
= "decor"; break;
1739 default: family
= "nil"; break;
1741 sprintf(tmp
, "{\\f%d\\f%s\\fprq%d\\fcharset%d %s;}",
1743 hlpfile
->fonts
[index
].LogFont
.lfPitchAndFamily
& 0x0F,
1744 hlpfile
->fonts
[index
].LogFont
.lfCharSet
,
1745 hlpfile
->fonts
[index
].LogFont
.lfFaceName
);
1746 if (!HLPFILE_RtfAddControl(rd
, tmp
)) return FALSE
;
1748 if (!HLPFILE_RtfAddControl(rd
, "}")) return FALSE
;
1749 /* generate color table */
1750 if (!HLPFILE_RtfAddControl(rd
, "{\\colortbl ;\\red0\\green128\\blue0;")) return FALSE
;
1751 for (index
= 0; index
< hlpfile
->numFonts
; index
++)
1753 sprintf(tmp
, "\\red%d\\green%d\\blue%d;",
1754 GetRValue(hlpfile
->fonts
[index
].color
),
1755 GetGValue(hlpfile
->fonts
[index
].color
),
1756 GetBValue(hlpfile
->fonts
[index
].color
));
1757 if (!HLPFILE_RtfAddControl(rd
, tmp
)) return FALSE
;
1759 if (!HLPFILE_RtfAddControl(rd
, "}")) return FALSE
;
1763 if (hlpfile
->version
<= 16)
1765 index
= (ref
- 0x0C) / hlpfile
->dsize
;
1766 offset
= (ref
- 0x0C) % hlpfile
->dsize
;
1770 index
= (ref
- 0x0C) >> 14;
1771 offset
= (ref
- 0x0C) & 0x3FFF;
1774 if (hlpfile
->version
<= 16 && index
!= old_index
&& old_index
!= -1)
1776 /* we jumped to the next block, adjust pointers */
1781 if (index
>= hlpfile
->topic_maplen
) {WINE_WARN("maplen\n"); break;}
1782 buf
= hlpfile
->topic_map
[index
] + offset
;
1783 if (buf
+ 0x15 >= hlpfile
->topic_end
) {WINE_WARN("extra\n"); break;}
1784 end
= min(buf
+ GET_UINT(buf
, 0), hlpfile
->topic_end
);
1785 if (index
!= old_index
) {offs
= 0; old_index
= index
;}
1790 if (count
++) goto done
;
1795 if (!HLPFILE_BrowseParagraph(page
, rd
, buf
, end
, &parlen
)) return FALSE
;
1796 if (relative
> index
* 0x8000 + offs
)
1797 rd
->char_pos_rel
= rd
->char_pos
;
1801 WINE_ERR("buf[0x14] = %x\n", buf
[0x14]);
1803 if (hlpfile
->version
<= 16)
1805 ref
+= GET_UINT(buf
, 0xc);
1806 if (GET_UINT(buf
, 0xc) == 0)
1810 ref
= GET_UINT(buf
, 0xc);
1811 } while (ref
!= 0xffffffff);
1813 page
->first_link
= rd
->first_link
;
1814 return HLPFILE_RtfAddControl(rd
, "}");
1817 /******************************************************************
1822 static BOOL
HLPFILE_ReadFont(HLPFILE
* hlpfile
)
1825 unsigned i
, len
, idx
;
1826 unsigned face_num
, dscr_num
, face_offset
, dscr_offset
;
1829 if (!HLPFILE_FindSubFile(hlpfile
, "|FONT", &ref
, &end
))
1831 WINE_WARN("no subfile FONT\n");
1832 hlpfile
->numFonts
= 0;
1833 hlpfile
->fonts
= NULL
;
1839 face_num
= GET_USHORT(ref
, 0);
1840 dscr_num
= GET_USHORT(ref
, 2);
1841 face_offset
= GET_USHORT(ref
, 4);
1842 dscr_offset
= GET_USHORT(ref
, 6);
1844 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1845 face_num
, face_offset
, dscr_num
, dscr_offset
);
1847 hlpfile
->numFonts
= dscr_num
;
1848 hlpfile
->fonts
= HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT
) * dscr_num
);
1850 len
= (dscr_offset
- face_offset
) / face_num
;
1851 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1852 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1853 for (i
= 0; i
< dscr_num
; i
++)
1855 flag
= ref
[dscr_offset
+ i
* 11 + 0];
1856 family
= ref
[dscr_offset
+ i
* 11 + 2];
1858 hlpfile
->fonts
[i
].LogFont
.lfHeight
= ref
[dscr_offset
+ i
* 11 + 1];
1859 hlpfile
->fonts
[i
].LogFont
.lfWidth
= 0;
1860 hlpfile
->fonts
[i
].LogFont
.lfEscapement
= 0;
1861 hlpfile
->fonts
[i
].LogFont
.lfOrientation
= 0;
1862 hlpfile
->fonts
[i
].LogFont
.lfWeight
= (flag
& 1) ? 700 : 400;
1863 hlpfile
->fonts
[i
].LogFont
.lfItalic
= (flag
& 2) != 0;
1864 hlpfile
->fonts
[i
].LogFont
.lfUnderline
= (flag
& 4) != 0;
1865 hlpfile
->fonts
[i
].LogFont
.lfStrikeOut
= (flag
& 8) != 0;
1866 hlpfile
->fonts
[i
].LogFont
.lfCharSet
= hlpfile
->charset
;
1867 hlpfile
->fonts
[i
].LogFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1868 hlpfile
->fonts
[i
].LogFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1869 hlpfile
->fonts
[i
].LogFont
.lfQuality
= DEFAULT_QUALITY
;
1870 hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
1874 case 0x01: hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
|= FF_MODERN
; break;
1875 case 0x02: hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
|= FF_ROMAN
; break;
1876 case 0x03: hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
|= FF_SWISS
; break;
1877 case 0x04: hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
|= FF_SCRIPT
; break;
1878 case 0x05: hlpfile
->fonts
[i
].LogFont
.lfPitchAndFamily
|= FF_DECORATIVE
; break;
1879 default: WINE_FIXME("Unknown family %u\n", family
);
1881 idx
= GET_USHORT(ref
, dscr_offset
+ i
* 11 + 3);
1885 memcpy(hlpfile
->fonts
[i
].LogFont
.lfFaceName
, ref
+ face_offset
+ idx
* len
, min(len
, LF_FACESIZE
- 1));
1886 hlpfile
->fonts
[i
].LogFont
.lfFaceName
[min(len
, LF_FACESIZE
- 1)] = '\0';
1890 WINE_FIXME("Too high face ref (%u/%u)\n", idx
, face_num
);
1891 strcpy(hlpfile
->fonts
[i
].LogFont
.lfFaceName
, "Helv");
1893 hlpfile
->fonts
[i
].hFont
= 0;
1894 hlpfile
->fonts
[i
].color
= RGB(ref
[dscr_offset
+ i
* 11 + 5],
1895 ref
[dscr_offset
+ i
* 11 + 6],
1896 ref
[dscr_offset
+ i
* 11 + 7]);
1897 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1898 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1904 X(4, "dblUnderline"),
1906 ref
[dscr_offset
+ i
* 11 + 1],
1908 hlpfile
->fonts
[i
].LogFont
.lfFaceName
, idx
,
1909 GET_UINT(ref
, dscr_offset
+ i
* 11 + 5) & 0x00FFFFFF);
1914 /***********************************************************************
1916 * HLPFILE_ReadFileToBuffer
1918 static BOOL
HLPFILE_ReadFileToBuffer(HLPFILE
* hlpfile
, HFILE hFile
)
1920 BYTE header
[16], dummy
[1];
1922 if (_hread(hFile
, header
, 16) != 16) {WINE_WARN("header\n"); return FALSE
;};
1925 if (GET_UINT(header
, 0) != 0x00035F3F)
1926 {WINE_WARN("wrong header\n"); return FALSE
;};
1928 hlpfile
->file_buffer_size
= GET_UINT(header
, 12);
1929 hlpfile
->file_buffer
= HeapAlloc(GetProcessHeap(), 0, hlpfile
->file_buffer_size
+ 1);
1930 if (!hlpfile
->file_buffer
) return FALSE
;
1932 memcpy(hlpfile
->file_buffer
, header
, 16);
1933 if (_hread(hFile
, hlpfile
->file_buffer
+ 16, hlpfile
->file_buffer_size
- 16) !=hlpfile
->file_buffer_size
- 16)
1934 {WINE_WARN("filesize1\n"); return FALSE
;};
1936 if (_hread(hFile
, dummy
, 1) != 0) WINE_WARN("filesize2\n");
1938 hlpfile
->file_buffer
[hlpfile
->file_buffer_size
] = '\0'; /* FIXME: was '0', sounds backwards to me */
1943 /***********************************************************************
1945 * HLPFILE_SystemCommands
1947 static BOOL
HLPFILE_SystemCommands(HLPFILE
* hlpfile
)
1949 BYTE
*buf
, *ptr
, *end
;
1950 HLPFILE_MACRO
*macro
, **m
;
1952 unsigned short magic
, minor
, major
, flags
;
1954 hlpfile
->lpszTitle
= NULL
;
1956 if (!HLPFILE_FindSubFile(hlpfile
, "|SYSTEM", &buf
, &end
)) return FALSE
;
1958 magic
= GET_USHORT(buf
+ 9, 0);
1959 minor
= GET_USHORT(buf
+ 9, 2);
1960 major
= GET_USHORT(buf
+ 9, 4);
1961 /* gen date on 4 bytes */
1962 flags
= GET_USHORT(buf
+ 9, 10);
1963 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1964 magic
, major
, minor
, flags
);
1965 if (magic
!= 0x036C || major
!= 1)
1966 {WINE_WARN("Wrong system header\n"); return FALSE
;}
1969 hlpfile
->tbsize
= 0x800;
1970 hlpfile
->compressed
= 0;
1972 else if (flags
== 0)
1974 hlpfile
->tbsize
= 0x1000;
1975 hlpfile
->compressed
= 0;
1977 else if (flags
== 4)
1979 hlpfile
->tbsize
= 0x1000;
1980 hlpfile
->compressed
= 1;
1984 hlpfile
->tbsize
= 0x800;
1985 hlpfile
->compressed
= 1;
1988 if (hlpfile
->compressed
)
1989 hlpfile
->dsize
= 0x4000;
1991 hlpfile
->dsize
= hlpfile
->tbsize
- 0x0C;
1993 hlpfile
->version
= minor
;
1994 hlpfile
->flags
= flags
;
1995 hlpfile
->charset
= DEFAULT_CHARSET
;
1997 if (hlpfile
->version
<= 16)
1999 char *str
= (char*)buf
+ 0x15;
2001 hlpfile
->lpszTitle
= HeapAlloc(GetProcessHeap(), 0, strlen(str
) + 1);
2002 if (!hlpfile
->lpszTitle
) return FALSE
;
2003 strcpy(hlpfile
->lpszTitle
, str
);
2004 WINE_TRACE("Title: %s\n", hlpfile
->lpszTitle
);
2005 /* Nothing more to parse */
2008 for (ptr
= buf
+ 0x15; ptr
+ 4 <= end
; ptr
+= GET_USHORT(ptr
, 2) + 4)
2010 char *str
= (char*) ptr
+ 4;
2011 switch (GET_USHORT(ptr
, 0))
2014 if (hlpfile
->lpszTitle
) {WINE_WARN("title\n"); break;}
2015 hlpfile
->lpszTitle
= HeapAlloc(GetProcessHeap(), 0, strlen(str
) + 1);
2016 if (!hlpfile
->lpszTitle
) return FALSE
;
2017 strcpy(hlpfile
->lpszTitle
, str
);
2018 WINE_TRACE("Title: %s\n", hlpfile
->lpszTitle
);
2022 if (hlpfile
->lpszCopyright
) {WINE_WARN("copyright\n"); break;}
2023 hlpfile
->lpszCopyright
= HeapAlloc(GetProcessHeap(), 0, strlen(str
) + 1);
2024 if (!hlpfile
->lpszCopyright
) return FALSE
;
2025 strcpy(hlpfile
->lpszCopyright
, str
);
2026 WINE_TRACE("Copyright: %s\n", hlpfile
->lpszCopyright
);
2030 if (GET_USHORT(ptr
, 2) != 4) {WINE_WARN("system3\n");break;}
2031 hlpfile
->contents_start
= GET_UINT(ptr
, 4);
2032 WINE_TRACE("Setting contents start at %08lx\n", hlpfile
->contents_start
);
2036 macro
= HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO
) + strlen(str
) + 1);
2038 p
= (char*)macro
+ sizeof(HLPFILE_MACRO
);
2040 macro
->lpszMacro
= p
;
2042 for (m
= &hlpfile
->first_macro
; *m
; m
= &(*m
)->next
);
2047 if (GET_USHORT(ptr
, 4 + 4) != 1)
2048 WINE_FIXME("More than one icon, picking up first\n");
2049 /* 0x16 is sizeof(CURSORICONDIR), see user32/user_private.h */
2050 hlpfile
->hIcon
= CreateIconFromResourceEx(ptr
+ 4 + 0x16,
2051 GET_USHORT(ptr
, 2) - 0x16, TRUE
,
2056 if (GET_USHORT(ptr
, 2) != 90) {WINE_WARN("system6\n");break;}
2058 if (hlpfile
->windows
)
2059 hlpfile
->windows
= HeapReAlloc(GetProcessHeap(), 0, hlpfile
->windows
,
2060 sizeof(HLPFILE_WINDOWINFO
) * ++hlpfile
->numWindows
);
2062 hlpfile
->windows
= HeapAlloc(GetProcessHeap(), 0,
2063 sizeof(HLPFILE_WINDOWINFO
) * ++hlpfile
->numWindows
);
2065 if (hlpfile
->windows
)
2067 HLPFILE_WINDOWINFO
* wi
= &hlpfile
->windows
[hlpfile
->numWindows
- 1];
2069 flags
= GET_USHORT(ptr
, 4);
2070 if (flags
& 0x0001) strcpy(wi
->type
, &str
[2]);
2071 else wi
->type
[0] = '\0';
2072 if (flags
& 0x0002) strcpy(wi
->name
, &str
[12]);
2073 else wi
->name
[0] = '\0';
2074 if (flags
& 0x0004) strcpy(wi
->caption
, &str
[21]);
2075 else lstrcpynA(wi
->caption
, hlpfile
->lpszTitle
, sizeof(wi
->caption
));
2076 wi
->origin
.x
= (flags
& 0x0008) ? GET_USHORT(ptr
, 76) : CW_USEDEFAULT
;
2077 wi
->origin
.y
= (flags
& 0x0010) ? GET_USHORT(ptr
, 78) : CW_USEDEFAULT
;
2078 wi
->size
.cx
= (flags
& 0x0020) ? GET_USHORT(ptr
, 80) : CW_USEDEFAULT
;
2079 wi
->size
.cy
= (flags
& 0x0040) ? GET_USHORT(ptr
, 82) : CW_USEDEFAULT
;
2080 wi
->style
= (flags
& 0x0080) ? GET_USHORT(ptr
, 84) : SW_SHOW
;
2081 wi
->win_style
= WS_OVERLAPPEDWINDOW
;
2082 wi
->sr_color
= (flags
& 0x0100) ? GET_UINT(ptr
, 86) : 0xFFFFFF;
2083 wi
->nsr_color
= (flags
& 0x0200) ? GET_UINT(ptr
, 90) : 0xFFFFFF;
2084 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",
2085 flags
& 0x0001 ? 'T' : 't',
2086 flags
& 0x0002 ? 'N' : 'n',
2087 flags
& 0x0004 ? 'C' : 'c',
2088 flags
& 0x0008 ? 'X' : 'x',
2089 flags
& 0x0010 ? 'Y' : 'y',
2090 flags
& 0x0020 ? 'W' : 'w',
2091 flags
& 0x0040 ? 'H' : 'h',
2092 flags
& 0x0080 ? 'S' : 's',
2093 wi
->type
, wi
->name
, wi
->caption
, wi
->origin
.x
, wi
->origin
.y
,
2094 wi
->size
.cx
, wi
->size
.cy
);
2098 WINE_WARN("Citation: '%s'\n", ptr
+ 4);
2101 hlpfile
->charset
= ptr
[4];
2102 WINE_TRACE("Charset: %d\n", hlpfile
->charset
);
2105 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr
, 0));
2108 if (!hlpfile
->lpszTitle
)
2109 hlpfile
->lpszTitle
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, 1);
2113 /***********************************************************************
2115 * HLPFILE_GetContext
2117 static BOOL
HLPFILE_GetContext(HLPFILE
*hlpfile
)
2122 if (!HLPFILE_FindSubFile(hlpfile
, "|CONTEXT", &cbuf
, &cend
))
2123 {WINE_WARN("context0\n"); return FALSE
;}
2126 hlpfile
->Context
= HeapAlloc(GetProcessHeap(), 0, clen
);
2127 if (!hlpfile
->Context
) return FALSE
;
2128 memcpy(hlpfile
->Context
, cbuf
, clen
);
2133 /***********************************************************************
2135 * HLPFILE_GetKeywords
2137 static BOOL
HLPFILE_GetKeywords(HLPFILE
*hlpfile
)
2142 if (!HLPFILE_FindSubFile(hlpfile
, "|KWBTREE", &cbuf
, &cend
)) return FALSE
;
2144 hlpfile
->kwbtree
= HeapAlloc(GetProcessHeap(), 0, clen
);
2145 if (!hlpfile
->kwbtree
) return FALSE
;
2146 memcpy(hlpfile
->kwbtree
, cbuf
, clen
);
2148 if (!HLPFILE_FindSubFile(hlpfile
, "|KWDATA", &cbuf
, &cend
))
2150 WINE_ERR("corrupted help file: kwbtree present but kwdata absent\n");
2151 HeapFree(GetProcessHeap(), 0, hlpfile
->kwbtree
);
2155 hlpfile
->kwdata
= HeapAlloc(GetProcessHeap(), 0, clen
);
2156 if (!hlpfile
->kwdata
)
2158 HeapFree(GetProcessHeap(), 0, hlpfile
->kwdata
);
2161 memcpy(hlpfile
->kwdata
, cbuf
, clen
);
2166 /***********************************************************************
2170 static BOOL
HLPFILE_GetMap(HLPFILE
*hlpfile
)
2173 unsigned entries
, i
;
2175 if (!HLPFILE_FindSubFile(hlpfile
, "|CTXOMAP", &cbuf
, &cend
))
2176 {WINE_WARN("no map section\n"); return FALSE
;}
2178 entries
= GET_USHORT(cbuf
, 9);
2179 hlpfile
->Map
= HeapAlloc(GetProcessHeap(), 0, entries
* sizeof(HLPFILE_MAP
));
2180 if (!hlpfile
->Map
) return FALSE
;
2181 hlpfile
->wMapLen
= entries
;
2182 for (i
= 0; i
< entries
; i
++)
2184 hlpfile
->Map
[i
].lMap
= GET_UINT(cbuf
+11,i
*8);
2185 hlpfile
->Map
[i
].offset
= GET_UINT(cbuf
+11,i
*8+4);
2190 /***********************************************************************
2194 static BOOL
HLPFILE_GetTOMap(HLPFILE
*hlpfile
)
2199 if (!HLPFILE_FindSubFile(hlpfile
, "|TOMAP", &cbuf
, &cend
))
2200 {WINE_WARN("no tomap section\n"); return FALSE
;}
2202 clen
= cend
- cbuf
- 9;
2203 hlpfile
->TOMap
= HeapAlloc(GetProcessHeap(), 0, clen
);
2204 if (!hlpfile
->TOMap
) return FALSE
;
2205 memcpy(hlpfile
->TOMap
, cbuf
+9, clen
);
2206 hlpfile
->wTOMapLen
= clen
/4;
2210 /***********************************************************************
2214 static void HLPFILE_DeleteMacro(HLPFILE_MACRO
* macro
)
2216 HLPFILE_MACRO
* next
;
2221 HeapFree(GetProcessHeap(), 0, macro
);
2226 /***********************************************************************
2230 static void HLPFILE_DeletePage(HLPFILE_PAGE
* page
)
2237 HLPFILE_DeleteMacro(page
->first_macro
);
2238 HeapFree(GetProcessHeap(), 0, page
);
2243 /***********************************************************************
2245 * HLPFILE_FreeHlpFile
2247 void HLPFILE_FreeHlpFile(HLPFILE
* hlpfile
)
2251 if (!hlpfile
|| --hlpfile
->wRefCount
> 0) return;
2253 if (hlpfile
->next
) hlpfile
->next
->prev
= hlpfile
->prev
;
2254 if (hlpfile
->prev
) hlpfile
->prev
->next
= hlpfile
->next
;
2255 else first_hlpfile
= hlpfile
->next
;
2257 if (hlpfile
->numFonts
)
2259 for (i
= 0; i
< hlpfile
->numFonts
; i
++)
2261 DeleteObject(hlpfile
->fonts
[i
].hFont
);
2263 HeapFree(GetProcessHeap(), 0, hlpfile
->fonts
);
2266 if (hlpfile
->numBmps
)
2268 for (i
= 0; i
< hlpfile
->numBmps
; i
++)
2270 DeleteObject(hlpfile
->bmps
[i
]);
2272 HeapFree(GetProcessHeap(), 0, hlpfile
->bmps
);
2275 HLPFILE_DeletePage(hlpfile
->first_page
);
2276 HLPFILE_DeleteMacro(hlpfile
->first_macro
);
2278 DestroyIcon(hlpfile
->hIcon
);
2279 if (hlpfile
->numWindows
) HeapFree(GetProcessHeap(), 0, hlpfile
->windows
);
2280 HeapFree(GetProcessHeap(), 0, hlpfile
->Context
);
2281 HeapFree(GetProcessHeap(), 0, hlpfile
->Map
);
2282 HeapFree(GetProcessHeap(), 0, hlpfile
->lpszTitle
);
2283 HeapFree(GetProcessHeap(), 0, hlpfile
->lpszCopyright
);
2284 HeapFree(GetProcessHeap(), 0, hlpfile
->file_buffer
);
2285 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_offsets
);
2286 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_buffer
);
2287 HeapFree(GetProcessHeap(), 0, hlpfile
->topic_map
);
2288 HeapFree(GetProcessHeap(), 0, hlpfile
->help_on_file
);
2289 HeapFree(GetProcessHeap(), 0, hlpfile
);
2292 /***********************************************************************
2294 * HLPFILE_UncompressLZ77_Phrases
2296 static BOOL
HLPFILE_UncompressLZ77_Phrases(HLPFILE
* hlpfile
)
2298 UINT i
, num
, dec_size
, head_size
;
2301 if (!HLPFILE_FindSubFile(hlpfile
, "|Phrases", &buf
, &end
)) return FALSE
;
2303 if (hlpfile
->version
<= 16)
2308 num
= hlpfile
->num_phrases
= GET_USHORT(buf
, 9);
2309 if (buf
+ 2 * num
+ 0x13 >= end
) {WINE_WARN("1a\n"); return FALSE
;};
2311 if (hlpfile
->version
<= 16)
2312 dec_size
= end
- buf
- 15 - 2 * num
;
2314 dec_size
= HLPFILE_UncompressedLZ77_Size(buf
+ 0x13 + 2 * num
, end
);
2316 hlpfile
->phrases_offsets
= HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num
+ 1));
2317 hlpfile
->phrases_buffer
= HeapAlloc(GetProcessHeap(), 0, dec_size
);
2318 if (!hlpfile
->phrases_offsets
|| !hlpfile
->phrases_buffer
)
2320 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_offsets
);
2321 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_buffer
);
2325 for (i
= 0; i
<= num
; i
++)
2326 hlpfile
->phrases_offsets
[i
] = GET_USHORT(buf
, head_size
+ 2 * i
) - 2 * num
- 2;
2328 if (hlpfile
->version
<= 16)
2329 memcpy(hlpfile
->phrases_buffer
, buf
+ 15 + 2*num
, dec_size
);
2331 HLPFILE_UncompressLZ77(buf
+ 0x13 + 2 * num
, end
, (BYTE
*)hlpfile
->phrases_buffer
);
2333 hlpfile
->hasPhrases
= TRUE
;
2337 /***********************************************************************
2339 * HLPFILE_Uncompress_Phrases40
2341 static BOOL
HLPFILE_Uncompress_Phrases40(HLPFILE
* hlpfile
)
2344 INT dec_size
, cpr_size
;
2345 BYTE
*buf_idx
, *end_idx
;
2346 BYTE
*buf_phs
, *end_phs
;
2347 ULONG
* ptr
, mask
= 0;
2349 unsigned short bc
, n
;
2351 if (!HLPFILE_FindSubFile(hlpfile
, "|PhrIndex", &buf_idx
, &end_idx
) ||
2352 !HLPFILE_FindSubFile(hlpfile
, "|PhrImage", &buf_phs
, &end_phs
)) return FALSE
;
2354 ptr
= (ULONG
*)(buf_idx
+ 9 + 28);
2355 bc
= GET_USHORT(buf_idx
, 9 + 24) & 0x0F;
2356 num
= hlpfile
->num_phrases
= GET_USHORT(buf_idx
, 9 + 4);
2358 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
2359 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
2360 GET_UINT(buf_idx
, 9 + 0),
2361 GET_UINT(buf_idx
, 9 + 4),
2362 GET_UINT(buf_idx
, 9 + 8),
2363 GET_UINT(buf_idx
, 9 + 12),
2364 GET_UINT(buf_idx
, 9 + 16),
2365 GET_UINT(buf_idx
, 9 + 20),
2366 GET_USHORT(buf_idx
, 9 + 24),
2367 GET_USHORT(buf_idx
, 9 + 26));
2369 dec_size
= GET_UINT(buf_idx
, 9 + 12);
2370 cpr_size
= GET_UINT(buf_idx
, 9 + 16);
2372 if (dec_size
!= cpr_size
&&
2373 dec_size
!= HLPFILE_UncompressedLZ77_Size(buf_phs
+ 9, end_phs
))
2375 WINE_WARN("size mismatch %u %u\n",
2376 dec_size
, HLPFILE_UncompressedLZ77_Size(buf_phs
+ 9, end_phs
));
2377 dec_size
= max(dec_size
, HLPFILE_UncompressedLZ77_Size(buf_phs
+ 9, end_phs
));
2380 hlpfile
->phrases_offsets
= HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num
+ 1));
2381 hlpfile
->phrases_buffer
= HeapAlloc(GetProcessHeap(), 0, dec_size
);
2382 if (!hlpfile
->phrases_offsets
|| !hlpfile
->phrases_buffer
)
2384 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_offsets
);
2385 HeapFree(GetProcessHeap(), 0, hlpfile
->phrases_buffer
);
2389 #define getbit() ((mask <<= 1) ? (*ptr & mask) != 0: (*++ptr & (mask=1)) != 0)
2391 hlpfile
->phrases_offsets
[0] = 0;
2392 ptr
--; /* as we'll first increment ptr because mask is 0 on first getbit() call */
2393 for (i
= 0; i
< num
; i
++)
2395 for (n
= 1; getbit(); n
+= 1 << bc
);
2397 if (bc
> 1 && getbit()) n
+= 2;
2398 if (bc
> 2 && getbit()) n
+= 4;
2399 if (bc
> 3 && getbit()) n
+= 8;
2400 if (bc
> 4 && getbit()) n
+= 16;
2401 hlpfile
->phrases_offsets
[i
+ 1] = hlpfile
->phrases_offsets
[i
] + n
;
2405 if (dec_size
== cpr_size
)
2406 memcpy(hlpfile
->phrases_buffer
, buf_phs
+ 9, dec_size
);
2408 HLPFILE_UncompressLZ77(buf_phs
+ 9, end_phs
, (BYTE
*)hlpfile
->phrases_buffer
);
2410 hlpfile
->hasPhrases40
= TRUE
;
2414 /***********************************************************************
2416 * HLPFILE_Uncompress_Topic
2418 static BOOL
HLPFILE_Uncompress_Topic(HLPFILE
* hlpfile
)
2420 BYTE
*buf
, *ptr
, *end
, *newptr
;
2421 unsigned int i
, newsize
= 0;
2422 unsigned int topic_size
;
2424 if (!HLPFILE_FindSubFile(hlpfile
, "|TOPIC", &buf
, &end
))
2425 {WINE_WARN("topic0\n"); return FALSE
;}
2427 buf
+= 9; /* Skip file header */
2428 topic_size
= end
- buf
;
2429 if (hlpfile
->compressed
)
2431 hlpfile
->topic_maplen
= (topic_size
- 1) / hlpfile
->tbsize
+ 1;
2433 for (i
= 0; i
< hlpfile
->topic_maplen
; i
++)
2435 ptr
= buf
+ i
* hlpfile
->tbsize
;
2437 /* I don't know why, it's necessary for printman.hlp */
2438 if (ptr
+ 0x44 > end
) ptr
= end
- 0x44;
2440 newsize
+= HLPFILE_UncompressedLZ77_Size(ptr
+ 0xc, min(end
, ptr
+ hlpfile
->tbsize
));
2443 hlpfile
->topic_map
= HeapAlloc(GetProcessHeap(), 0,
2444 hlpfile
->topic_maplen
* sizeof(hlpfile
->topic_map
[0]) + newsize
);
2445 if (!hlpfile
->topic_map
) return FALSE
;
2446 newptr
= (BYTE
*)(hlpfile
->topic_map
+ hlpfile
->topic_maplen
);
2447 hlpfile
->topic_end
= newptr
+ newsize
;
2449 for (i
= 0; i
< hlpfile
->topic_maplen
; i
++)
2451 ptr
= buf
+ i
* hlpfile
->tbsize
;
2452 if (ptr
+ 0x44 > end
) ptr
= end
- 0x44;
2454 hlpfile
->topic_map
[i
] = newptr
;
2455 newptr
= HLPFILE_UncompressLZ77(ptr
+ 0xc, min(end
, ptr
+ hlpfile
->tbsize
), newptr
);
2460 /* basically, we need to copy the TopicBlockSize byte pages
2461 * (removing the first 0x0C) in one single area in memory
2463 hlpfile
->topic_maplen
= (topic_size
- 1) / hlpfile
->tbsize
+ 1;
2464 hlpfile
->topic_map
= HeapAlloc(GetProcessHeap(), 0,
2465 hlpfile
->topic_maplen
* (sizeof(hlpfile
->topic_map
[0]) + hlpfile
->dsize
));
2466 if (!hlpfile
->topic_map
) return FALSE
;
2467 newptr
= (BYTE
*)(hlpfile
->topic_map
+ hlpfile
->topic_maplen
);
2468 hlpfile
->topic_end
= newptr
+ topic_size
;
2470 for (i
= 0; i
< hlpfile
->topic_maplen
; i
++)
2472 hlpfile
->topic_map
[i
] = newptr
+ i
* hlpfile
->dsize
;
2473 memcpy(hlpfile
->topic_map
[i
], buf
+ i
* hlpfile
->tbsize
+ 0x0C, hlpfile
->dsize
);
2479 /***********************************************************************
2483 static BOOL
HLPFILE_AddPage(HLPFILE
*hlpfile
, const BYTE
*buf
, const BYTE
*end
, unsigned ref
, unsigned offset
)
2487 UINT titlesize
, blocksize
, datalen
;
2489 HLPFILE_MACRO
*macro
;
2491 blocksize
= GET_UINT(buf
, 0);
2492 datalen
= GET_UINT(buf
, 0x10);
2493 title
= buf
+ datalen
;
2494 if (title
> end
) {WINE_WARN("page2\n"); return FALSE
;};
2496 titlesize
= GET_UINT(buf
, 4);
2497 page
= HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE
) + titlesize
+ 1);
2498 if (!page
) return FALSE
;
2499 page
->lpszTitle
= (char*)page
+ sizeof(HLPFILE_PAGE
);
2501 if (titlesize
> blocksize
- datalen
)
2503 /* need to decompress */
2504 if (hlpfile
->hasPhrases
)
2505 HLPFILE_Uncompress2(hlpfile
, title
, end
, (BYTE
*)page
->lpszTitle
, (BYTE
*)page
->lpszTitle
+ titlesize
);
2506 else if (hlpfile
->hasPhrases40
)
2507 HLPFILE_Uncompress3(hlpfile
, page
->lpszTitle
, page
->lpszTitle
+ titlesize
, title
, end
);
2510 WINE_FIXME("Text size is too long, splitting\n");
2511 titlesize
= blocksize
- datalen
;
2512 memcpy(page
->lpszTitle
, title
, titlesize
);
2516 memcpy(page
->lpszTitle
, title
, titlesize
);
2518 page
->lpszTitle
[titlesize
] = '\0';
2520 if (hlpfile
->first_page
)
2522 hlpfile
->last_page
->next
= page
;
2523 page
->prev
= hlpfile
->last_page
;
2524 hlpfile
->last_page
= page
;
2528 hlpfile
->first_page
= page
;
2529 hlpfile
->last_page
= page
;
2533 page
->file
= hlpfile
;
2535 page
->first_macro
= NULL
;
2536 page
->first_link
= NULL
;
2537 page
->wNumber
= GET_UINT(buf
, 0x21);
2538 page
->offset
= offset
;
2539 page
->reference
= ref
;
2541 page
->browse_bwd
= GET_UINT(buf
, 0x19);
2542 page
->browse_fwd
= GET_UINT(buf
, 0x1D);
2544 if (hlpfile
->version
<= 16)
2546 if (page
->browse_bwd
== 0xFFFF || page
->browse_bwd
== 0xFFFFFFFF)
2547 page
->browse_bwd
= 0xFFFFFFFF;
2549 page
->browse_bwd
= hlpfile
->TOMap
[page
->browse_bwd
];
2551 if (page
->browse_fwd
== 0xFFFF || page
->browse_fwd
== 0xFFFFFFFF)
2552 page
->browse_fwd
= 0xFFFFFFFF;
2554 page
->browse_fwd
= hlpfile
->TOMap
[page
->browse_fwd
];
2557 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
2558 page
->wNumber
, page
->lpszTitle
,
2559 page
->browse_bwd
, page
->offset
, page
->browse_fwd
);
2561 /* now load macros */
2562 ptr
= page
->lpszTitle
+ strlen(page
->lpszTitle
) + 1;
2563 while (ptr
< page
->lpszTitle
+ titlesize
)
2565 unsigned len
= strlen(ptr
);
2568 WINE_TRACE("macro: %s\n", ptr
);
2569 macro
= HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO
) + len
+ 1);
2570 macro
->lpszMacro
= macro_str
= (char*)(macro
+ 1);
2571 memcpy(macro_str
, ptr
, len
+ 1);
2572 /* FIXME: shall we really link macro in reverse order ??
2573 * may produce strange results when played at page opening
2575 macro
->next
= page
->first_macro
;
2576 page
->first_macro
= macro
;
2583 /***********************************************************************
2585 * HLPFILE_SkipParagraph
2587 static BOOL
HLPFILE_SkipParagraph(HLPFILE
*hlpfile
, const BYTE
*buf
, const BYTE
*end
, unsigned* len
)
2591 if (!hlpfile
->first_page
) {WINE_WARN("no page\n"); return FALSE
;};
2592 if (buf
+ 0x19 > end
) {WINE_WARN("header too small\n"); return FALSE
;};
2595 if (buf
[0x14] == 0x20 || buf
[0x14] == 0x23)
2598 *len
= fetch_ushort(&tmp
);
2600 else *len
= end
-buf
-15;
2605 /***********************************************************************
2607 * HLPFILE_DoReadHlpFile
2609 static BOOL
HLPFILE_DoReadHlpFile(HLPFILE
*hlpfile
, LPCSTR lpszPath
)
2616 unsigned index
, old_index
, offset
, len
, offs
, topicoffset
;
2618 hFile
= OpenFile(lpszPath
, &ofs
, OF_READ
);
2619 if (hFile
== HFILE_ERROR
) return FALSE
;
2621 ret
= HLPFILE_ReadFileToBuffer(hlpfile
, hFile
);
2623 if (!ret
) return FALSE
;
2625 if (!HLPFILE_SystemCommands(hlpfile
)) return FALSE
;
2627 if (hlpfile
->version
<= 16 && !HLPFILE_GetTOMap(hlpfile
)) return FALSE
;
2629 /* load phrases support */
2630 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile
))
2631 HLPFILE_Uncompress_Phrases40(hlpfile
);
2633 if (!HLPFILE_Uncompress_Topic(hlpfile
)) return FALSE
;
2634 if (!HLPFILE_ReadFont(hlpfile
)) return FALSE
;
2642 if (hlpfile
->version
<= 16)
2644 index
= (ref
- 0x0C) / hlpfile
->dsize
;
2645 offset
= (ref
- 0x0C) % hlpfile
->dsize
;
2649 index
= (ref
- 0x0C) >> 14;
2650 offset
= (ref
- 0x0C) & 0x3FFF;
2653 if (hlpfile
->version
<= 16 && index
!= old_index
&& old_index
!= -1)
2655 /* we jumped to the next block, adjust pointers */
2660 WINE_TRACE("ref=%08x => [%u/%u]\n", ref
, index
, offset
);
2662 if (index
>= hlpfile
->topic_maplen
) {WINE_WARN("maplen\n"); break;}
2663 buf
= hlpfile
->topic_map
[index
] + offset
;
2664 if (buf
+ 0x15 >= hlpfile
->topic_end
) {WINE_WARN("extra\n"); break;}
2665 end
= min(buf
+ GET_UINT(buf
, 0), hlpfile
->topic_end
);
2666 if (index
!= old_index
) {offs
= 0; old_index
= index
;}
2671 if (hlpfile
->version
<= 16)
2672 topicoffset
= ref
+ index
* 12;
2674 topicoffset
= index
* 0x8000 + offs
;
2675 if (!HLPFILE_AddPage(hlpfile
, buf
, end
, ref
, topicoffset
)) return FALSE
;
2681 if (!HLPFILE_SkipParagraph(hlpfile
, buf
, end
, &len
)) return FALSE
;
2686 WINE_ERR("buf[0x14] = %x\n", buf
[0x14]);
2689 if (hlpfile
->version
<= 16)
2691 ref
+= GET_UINT(buf
, 0xc);
2692 if (GET_UINT(buf
, 0xc) == 0)
2696 ref
= GET_UINT(buf
, 0xc);
2697 } while (ref
!= 0xffffffff);
2699 HLPFILE_GetKeywords(hlpfile
);
2700 HLPFILE_GetMap(hlpfile
);
2701 if (hlpfile
->version
<= 16) return TRUE
;
2702 return HLPFILE_GetContext(hlpfile
);
2705 /***********************************************************************
2707 * HLPFILE_ReadHlpFile
2709 HLPFILE
*HLPFILE_ReadHlpFile(LPCSTR lpszPath
)
2713 for (hlpfile
= first_hlpfile
; hlpfile
; hlpfile
= hlpfile
->next
)
2715 if (!strcmp(lpszPath
, hlpfile
->lpszPath
))
2717 hlpfile
->wRefCount
++;
2722 hlpfile
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2723 sizeof(HLPFILE
) + strlen(lpszPath
) + 1);
2724 if (!hlpfile
) return 0;
2726 hlpfile
->lpszPath
= (char*)hlpfile
+ sizeof(HLPFILE
);
2727 hlpfile
->contents_start
= 0xFFFFFFFF;
2728 hlpfile
->next
= first_hlpfile
;
2729 hlpfile
->wRefCount
= 1;
2731 strcpy(hlpfile
->lpszPath
, lpszPath
);
2733 first_hlpfile
= hlpfile
;
2734 if (hlpfile
->next
) hlpfile
->next
->prev
= hlpfile
;
2736 if (!HLPFILE_DoReadHlpFile(hlpfile
, lpszPath
))
2738 HLPFILE_FreeHlpFile(hlpfile
);